@jmruthers/pace-core 0.5.193 → 0.6.2

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 (577) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +7 -1
  3. package/cursor-rules/00-pace-core-compliance.mdc +299 -0
  4. package/cursor-rules/01-standards-compliance.mdc +244 -0
  5. package/cursor-rules/02-project-structure.mdc +200 -0
  6. package/cursor-rules/03-solid-principles.mdc +222 -0
  7. package/cursor-rules/04-testing-standards.mdc +268 -0
  8. package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
  9. package/cursor-rules/06-code-quality.mdc +309 -0
  10. package/cursor-rules/07-tech-stack-compliance.mdc +214 -0
  11. package/cursor-rules/08-markup-quality.mdc +452 -0
  12. package/cursor-rules/CHANGELOG.md +119 -0
  13. package/cursor-rules/README.md +192 -0
  14. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
  15. package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-BMRU8a1j.d.ts} +34 -2
  16. package/dist/{DataTable-5FU7IESH.js → DataTable-TPTKCX4D.js} +10 -9
  17. package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +385 -261
  18. package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
  19. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
  20. package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
  21. package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
  22. package/dist/chunk-24UVZUZG.js.map +1 -0
  23. package/dist/{chunk-HWIIPPNI.js → chunk-2UOI2FG5.js} +20 -20
  24. package/dist/chunk-2UOI2FG5.js.map +1 -0
  25. package/dist/{chunk-E3SPN4VZ 5.js → chunk-3XC4CPTD.js} +4345 -3986
  26. package/dist/chunk-3XC4CPTD.js.map +1 -0
  27. package/dist/{chunk-7EQTDTTJ.js → chunk-6J4GEEJR.js} +172 -45
  28. package/dist/chunk-6J4GEEJR.js.map +1 -0
  29. package/dist/{chunk-6C4YBBJM 5.js → chunk-6SOIHG6Z.js} +1 -1
  30. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  31. package/dist/{chunk-7FLMSG37.js → chunk-EHMR7VYL.js} +25 -25
  32. package/dist/chunk-EHMR7VYL.js.map +1 -0
  33. package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
  34. package/dist/chunk-F2IMUDXZ.js.map +1 -0
  35. package/dist/{chunk-QWWZ5CAQ.js → chunk-FFQEQTNW.js} +7 -9
  36. package/dist/chunk-FFQEQTNW.js.map +1 -0
  37. package/dist/chunk-FMUCXFII.js +76 -0
  38. package/dist/chunk-FMUCXFII.js.map +1 -0
  39. package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
  40. package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
  41. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  42. package/dist/chunk-L4OXEN46.js.map +1 -0
  43. package/dist/{chunk-R77UEZ4E 3.js → chunk-M43Y4SSO.js} +1 -1
  44. package/dist/chunk-M43Y4SSO.js.map +1 -0
  45. package/dist/{chunk-IIELH4DL.js → chunk-MMZ7JXPU.js} +60 -223
  46. package/dist/chunk-MMZ7JXPU.js.map +1 -0
  47. package/dist/{chunk-NOAYCWCX 5.js → chunk-NECFR5MM.js} +394 -312
  48. package/dist/chunk-NECFR5MM.js.map +1 -0
  49. package/dist/{chunk-BC4IJKSL.js → chunk-SFZUDBL5.js} +40 -4
  50. package/dist/chunk-SFZUDBL5.js.map +1 -0
  51. package/dist/{chunk-XNXXZ43G.js → chunk-XWQCNGTQ.js} +748 -364
  52. package/dist/chunk-XWQCNGTQ.js.map +1 -0
  53. package/dist/components.d.ts +6 -6
  54. package/dist/components.js +15 -12
  55. package/dist/components.js.map +1 -1
  56. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  57. package/dist/hooks.d.ts +59 -126
  58. package/dist/hooks.js +19 -28
  59. package/dist/hooks.js.map +1 -1
  60. package/dist/index.d.ts +63 -16
  61. package/dist/index.js +23 -24
  62. package/dist/index.js.map +1 -1
  63. package/dist/providers.d.ts +21 -3
  64. package/dist/providers.js +2 -2
  65. package/dist/rbac/index.d.ts +146 -115
  66. package/dist/rbac/index.js +8 -11
  67. package/dist/theming/runtime.d.ts +1 -13
  68. package/dist/theming/runtime.js +1 -1
  69. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  70. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  71. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  72. package/dist/types.d.ts +2 -2
  73. package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +34 -4
  74. package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
  75. package/dist/utils.d.ts +4 -5
  76. package/dist/utils.js +15 -15
  77. package/dist/utils.js.map +1 -1
  78. package/docs/api/README.md +7 -1
  79. package/docs/api/classes/ColumnFactory.md +8 -8
  80. package/docs/api/classes/InvalidScopeError.md +4 -4
  81. package/docs/api/classes/Logger.md +1 -1
  82. package/docs/api/classes/MissingUserContextError.md +4 -4
  83. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  84. package/docs/api/classes/PermissionDeniedError.md +4 -4
  85. package/docs/api/classes/RBACAuditManager.md +1 -1
  86. package/docs/api/classes/RBACCache.md +1 -1
  87. package/docs/api/classes/RBACEngine.md +1 -1
  88. package/docs/api/classes/RBACError.md +4 -4
  89. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  90. package/docs/api/classes/SecureSupabaseClient.md +18 -15
  91. package/docs/api/classes/StorageUtils.md +1 -1
  92. package/docs/api/enums/FileCategory.md +1 -1
  93. package/docs/api/enums/LogLevel.md +1 -1
  94. package/docs/api/enums/RBACErrorCode.md +1 -1
  95. package/docs/api/enums/RPCFunction.md +1 -1
  96. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  97. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  98. package/docs/api/interfaces/AggregateConfig.md +4 -4
  99. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  100. package/docs/api/interfaces/AvatarProps.md +1 -1
  101. package/docs/api/interfaces/BadgeProps.md +9 -2
  102. package/docs/api/interfaces/ButtonProps.md +7 -4
  103. package/docs/api/interfaces/CalendarProps.md +8 -5
  104. package/docs/api/interfaces/CardProps.md +8 -5
  105. package/docs/api/interfaces/ColorPalette.md +1 -1
  106. package/docs/api/interfaces/ColorShade.md +1 -1
  107. package/docs/api/interfaces/ComplianceResult.md +1 -1
  108. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  109. package/docs/api/interfaces/DataRecord.md +1 -1
  110. package/docs/api/interfaces/DataTableAction.md +24 -21
  111. package/docs/api/interfaces/DataTableColumn.md +31 -31
  112. package/docs/api/interfaces/DataTableProps.md +1 -1
  113. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  114. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  115. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  116. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  117. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  118. package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
  119. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
  120. package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
  121. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  122. package/docs/api/interfaces/ExportColumn.md +1 -1
  123. package/docs/api/interfaces/ExportOptions.md +8 -8
  124. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  125. package/docs/api/interfaces/FileMetadata.md +1 -1
  126. package/docs/api/interfaces/FileReference.md +1 -1
  127. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  128. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  129. package/docs/api/interfaces/FileUploadProps.md +26 -23
  130. package/docs/api/interfaces/FooterProps.md +10 -8
  131. package/docs/api/interfaces/FormFieldProps.md +10 -10
  132. package/docs/api/interfaces/FormProps.md +1 -1
  133. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  134. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  135. package/docs/api/interfaces/InputProps.md +7 -4
  136. package/docs/api/interfaces/LabelProps.md +1 -1
  137. package/docs/api/interfaces/LoggerConfig.md +1 -1
  138. package/docs/api/interfaces/LoginFormProps.md +14 -11
  139. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  140. package/docs/api/interfaces/NavigationContextType.md +1 -1
  141. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  142. package/docs/api/interfaces/NavigationItem.md +11 -11
  143. package/docs/api/interfaces/NavigationMenuProps.md +15 -15
  144. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  145. package/docs/api/interfaces/Organisation.md +1 -1
  146. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  147. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  148. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  149. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  150. package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
  151. package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
  152. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  153. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  154. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  155. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  156. package/docs/api/interfaces/PaletteData.md +1 -1
  157. package/docs/api/interfaces/ParsedAddress.md +1 -1
  158. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  159. package/docs/api/interfaces/ProgressProps.md +1 -1
  160. package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
  161. package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
  162. package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
  163. package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
  164. package/docs/api/interfaces/QuickFix.md +1 -1
  165. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  166. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  167. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  168. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  169. package/docs/api/interfaces/RBACConfig.md +1 -1
  170. package/docs/api/interfaces/RBACContext.md +1 -1
  171. package/docs/api/interfaces/RBACLogger.md +1 -1
  172. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  173. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  174. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  175. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  176. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  177. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  178. package/docs/api/interfaces/RBACResult.md +1 -1
  179. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  180. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  181. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  182. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  183. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  184. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  185. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  186. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  187. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  188. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  189. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  190. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  191. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  192. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  193. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  194. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  195. package/docs/api/interfaces/RouteConfig.md +1 -1
  196. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  197. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  198. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  199. package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
  200. package/docs/api/interfaces/SetupIssue.md +1 -1
  201. package/docs/api/interfaces/StorageConfig.md +1 -1
  202. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  203. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  204. package/docs/api/interfaces/StorageListOptions.md +1 -1
  205. package/docs/api/interfaces/StorageListResult.md +1 -1
  206. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  207. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  208. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  209. package/docs/api/interfaces/StyleImport.md +1 -1
  210. package/docs/api/interfaces/SwitchProps.md +1 -1
  211. package/docs/api/interfaces/TabsContentProps.md +1 -1
  212. package/docs/api/interfaces/TabsListProps.md +1 -1
  213. package/docs/api/interfaces/TabsProps.md +1 -1
  214. package/docs/api/interfaces/TabsTriggerProps.md +3 -3
  215. package/docs/api/interfaces/TextareaProps.md +1 -1
  216. package/docs/api/interfaces/ToastActionElement.md +4 -1
  217. package/docs/api/interfaces/ToastProps.md +1 -1
  218. package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
  219. package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
  220. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  221. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  222. package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
  223. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  224. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  225. package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
  226. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  227. package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
  228. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  229. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
  230. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
  231. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  232. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  233. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  234. package/docs/api/interfaces/UserEventAccess.md +14 -11
  235. package/docs/api/interfaces/UserMenuProps.md +8 -6
  236. package/docs/api/interfaces/UserProfile.md +1 -1
  237. package/docs/api/modules.md +575 -634
  238. package/docs/architecture/database-schema-requirements.md +161 -0
  239. package/docs/core-concepts/rbac-system.md +3 -3
  240. package/docs/documentation-index.md +2 -4
  241. package/docs/getting-started/cursor-rules.md +263 -0
  242. package/docs/getting-started/installation-guide.md +6 -1
  243. package/docs/getting-started/quick-start.md +6 -1
  244. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  245. package/docs/migration/MIGRATION_GUIDE.md +6 -28
  246. package/docs/migration/README.md +52 -6
  247. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  248. package/docs/migration/V0.6.0_REACT_19_MIGRATION.md +227 -0
  249. package/docs/migration/database-changes-december-2025.md +3 -3
  250. package/docs/rbac/event-based-apps.md +1 -1
  251. package/docs/rbac/getting-started.md +1 -1
  252. package/docs/rbac/quick-start.md +1 -1
  253. package/docs/standards/README.md +40 -0
  254. package/docs/troubleshooting/migration.md +4 -4
  255. package/examples/PublicPages/PublicEventPage.tsx +1 -1
  256. package/package.json +12 -6
  257. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  258. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  259. package/scripts/audit/core/checks/bundle.cjs +142 -0
  260. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +737 -691
  261. package/scripts/audit/core/checks/config.cjs +54 -0
  262. package/scripts/audit/core/checks/coverage.cjs +84 -0
  263. package/scripts/audit/core/checks/dependencies.cjs +454 -0
  264. package/scripts/audit/core/checks/documentation.cjs +203 -0
  265. package/scripts/audit/core/checks/environment.cjs +128 -0
  266. package/scripts/audit/core/checks/error-handling.cjs +299 -0
  267. package/scripts/audit/core/checks/forms.cjs +172 -0
  268. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  269. package/scripts/audit/core/checks/hooks.cjs +334 -0
  270. package/scripts/audit/core/checks/imports.cjs +244 -0
  271. package/scripts/audit/core/checks/performance.cjs +325 -0
  272. package/scripts/audit/core/checks/routes.cjs +117 -0
  273. package/scripts/audit/core/checks/state.cjs +130 -0
  274. package/scripts/audit/core/checks/structure.cjs +65 -0
  275. package/scripts/audit/core/checks/style.cjs +584 -0
  276. package/scripts/audit/core/checks/testing.cjs +122 -0
  277. package/scripts/audit/core/checks/typescript.cjs +61 -0
  278. package/scripts/audit/core/scanner.cjs +199 -0
  279. package/scripts/audit/core/utils.cjs +137 -0
  280. package/scripts/audit/index.cjs +223 -0
  281. package/scripts/audit/reporters/console.cjs +151 -0
  282. package/scripts/audit/reporters/json.cjs +54 -0
  283. package/scripts/audit/reporters/markdown.cjs +124 -0
  284. package/scripts/audit-consuming-app.cjs +86 -0
  285. package/scripts/build-docs/build-decision.js +240 -0
  286. package/scripts/build-docs/cache-utils.js +105 -0
  287. package/scripts/build-docs/content-normalization.js +150 -0
  288. package/scripts/build-docs/file-utils.js +105 -0
  289. package/scripts/build-docs/git-utils.js +86 -0
  290. package/scripts/build-docs/hash-utils.js +116 -0
  291. package/scripts/build-docs/typedoc-runner.js +220 -0
  292. package/scripts/build-docs-incremental.js +77 -913
  293. package/scripts/install-cursor-rules.cjs +236 -0
  294. package/scripts/utils/command-runner.js +16 -11
  295. package/scripts/validate-formats.js +61 -56
  296. package/scripts/validate-master.js +74 -69
  297. package/scripts/validate-pre-publish.js +70 -65
  298. package/src/__tests__/helpers/test-providers.tsx +1 -1
  299. package/src/__tests__/helpers/test-utils.tsx +1 -1
  300. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  301. package/src/components/Alert/Alert.test.tsx +12 -18
  302. package/src/components/Alert/Alert.tsx +5 -7
  303. package/src/components/Avatar/Avatar.test.tsx +4 -4
  304. package/src/components/Badge/Badge.tsx +16 -4
  305. package/src/components/Button/Button.tsx +27 -4
  306. package/src/components/Calendar/Calendar.tsx +9 -3
  307. package/src/components/Card/Card.tsx +4 -0
  308. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  309. package/src/components/Checkbox/Checkbox.tsx +2 -2
  310. package/src/components/DataTable/DataTable.test.tsx +57 -93
  311. package/src/components/DataTable/DataTable.tsx +40 -6
  312. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  313. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +29 -7
  314. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
  315. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  316. package/src/components/DataTable/components/AccessDeniedPage.tsx +17 -26
  317. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  318. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
  319. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  320. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  321. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  322. package/src/components/DataTable/components/DataTableCore.tsx +200 -561
  323. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  324. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  325. package/src/components/DataTable/components/DataTableModals.tsx +9 -1
  326. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  327. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  328. package/src/components/DataTable/components/EditFields.tsx +307 -0
  329. package/src/components/DataTable/components/EditableRow.tsx +9 -1
  330. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  331. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  332. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  333. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  334. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  335. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  336. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  337. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  338. package/src/components/DataTable/components/UnifiedTableBody.tsx +62 -852
  339. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  340. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  341. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
  342. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
  343. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
  344. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
  345. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
  346. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
  347. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
  348. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  349. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  350. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  351. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  352. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  353. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  354. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  355. package/src/components/DataTable/hooks/useColumnReordering.ts +14 -2
  356. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  357. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  358. package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
  359. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  360. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  361. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
  362. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  363. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  364. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  365. package/src/components/DataTable/styles.ts +6 -6
  366. package/src/components/DataTable/types.ts +6 -10
  367. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  368. package/src/components/DataTable/utils/debugTools.ts +18 -113
  369. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  370. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  371. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  372. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  373. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  374. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
  375. package/src/components/Dialog/Dialog.tsx +8 -7
  376. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  377. package/src/components/ErrorBoundary/ErrorBoundary.tsx +46 -6
  378. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  379. package/src/components/ErrorBoundary/index.ts +27 -2
  380. package/src/components/EventSelector/EventSelector.tsx +4 -1
  381. package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
  382. package/src/components/FileDisplay/FileDisplay.tsx +32 -18
  383. package/src/components/FileUpload/FileUpload.tsx +22 -2
  384. package/src/components/Footer/Footer.test.tsx +16 -16
  385. package/src/components/Footer/Footer.tsx +15 -12
  386. package/src/components/Form/Form.test.tsx +36 -15
  387. package/src/components/Form/Form.tsx +31 -26
  388. package/src/components/Header/Header.tsx +22 -11
  389. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
  390. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
  391. package/src/components/Input/Input.test.tsx +2 -2
  392. package/src/components/Input/Input.tsx +36 -34
  393. package/src/components/Label/Label.tsx +1 -1
  394. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  395. package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
  396. package/src/components/LoginForm/LoginForm.test.tsx +42 -42
  397. package/src/components/LoginForm/LoginForm.tsx +12 -8
  398. package/src/components/NavigationMenu/NavigationMenu.tsx +15 -514
  399. package/src/components/NavigationMenu/types.ts +56 -0
  400. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  401. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
  402. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
  403. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +54 -52
  404. package/src/components/PaceAppLayout/PaceAppLayout.tsx +33 -12
  405. package/src/components/PaceAppLayout/README.md +1 -1
  406. package/src/components/PaceAppLayout/test-setup.tsx +1 -2
  407. package/src/components/PaceLoginPage/PaceLoginPage.tsx +4 -1
  408. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
  409. package/src/components/PasswordChange/PasswordChangeForm.tsx +10 -1
  410. package/src/components/Progress/Progress.tsx +1 -1
  411. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  412. package/src/components/PublicLayout/PublicPageLayout.tsx +3 -6
  413. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  414. package/src/components/Select/Select.tsx +95 -438
  415. package/src/components/Select/context.ts +23 -0
  416. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  417. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  418. package/src/components/Select/hooks/useSelectState.ts +104 -0
  419. package/src/components/Select/index.ts +9 -1
  420. package/src/components/Select/types.ts +123 -0
  421. package/src/components/Select/utils/text.ts +26 -0
  422. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +5 -6
  423. package/src/components/Switch/Switch.tsx +4 -4
  424. package/src/components/Table/Table.tsx +1 -1
  425. package/src/components/Tabs/Tabs.tsx +1 -1
  426. package/src/components/Textarea/Textarea.tsx +27 -29
  427. package/src/components/Toast/Toast.tsx +5 -1
  428. package/src/components/Tooltip/Tooltip.tsx +3 -3
  429. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  430. package/src/components/UserMenu/UserMenu.tsx +22 -19
  431. package/src/components/index.ts +2 -2
  432. package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
  433. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  434. package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
  435. package/src/hooks/index.ts +1 -2
  436. package/src/hooks/public/usePublicEvent.ts +5 -1
  437. package/src/hooks/public/usePublicEventLogo.ts +5 -1
  438. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  439. package/src/hooks/public/usePublicRouteParams.ts +5 -1
  440. package/src/hooks/services/useAuth.ts +32 -0
  441. package/src/hooks/services/useCurrentEvent.ts +6 -0
  442. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  443. package/src/hooks/useDataTableState.ts +8 -18
  444. package/src/hooks/useDebounce.ts +9 -0
  445. package/src/hooks/useEventTheme.ts +6 -0
  446. package/src/hooks/useFileDisplay.ts +4 -0
  447. package/src/hooks/useFileReference.ts +25 -7
  448. package/src/hooks/useFileUrl.ts +11 -1
  449. package/src/hooks/useFocusManagement.ts +16 -2
  450. package/src/hooks/useFocusTrap.ts +7 -4
  451. package/src/hooks/useFormDialog.ts +8 -7
  452. package/src/hooks/useInactivityTracker.ts +4 -1
  453. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  454. package/src/hooks/useOrganisationPermissions.ts +4 -0
  455. package/src/hooks/useOrganisationSecurity.ts +4 -0
  456. package/src/hooks/usePerformanceMonitor.ts +4 -0
  457. package/src/hooks/usePermissionCache.ts +8 -1
  458. package/src/hooks/useQueryCache.ts +12 -1
  459. package/src/hooks/useSessionRestoration.ts +4 -0
  460. package/src/hooks/useStorage.ts +4 -0
  461. package/src/hooks/useToast.ts +3 -3
  462. package/src/index.ts +2 -1
  463. package/src/providers/__tests__/OrganisationProvider.test.tsx +115 -49
  464. package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
  465. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
  466. package/src/providers/services/AuthServiceProvider.tsx +18 -0
  467. package/src/providers/services/EventServiceProvider.tsx +18 -0
  468. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  469. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  470. package/src/providers/services/UnifiedAuthProvider.tsx +58 -22
  471. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +33 -7
  472. package/src/rbac/README.md +1 -1
  473. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +26 -26
  474. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  475. package/src/rbac/adapters.tsx +14 -5
  476. package/src/rbac/api.ts +100 -67
  477. package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
  478. package/src/rbac/components/NavigationGuard.tsx +1 -1
  479. package/src/rbac/components/NavigationProvider.tsx +5 -2
  480. package/src/rbac/components/PagePermissionGuard.tsx +158 -18
  481. package/src/rbac/components/PagePermissionProvider.tsx +1 -1
  482. package/src/rbac/components/PermissionEnforcer.tsx +1 -1
  483. package/src/rbac/components/RoleBasedRouter.tsx +6 -2
  484. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  485. package/src/rbac/components/SecureDataProvider.tsx +21 -6
  486. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  487. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  488. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  489. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  490. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  491. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  492. package/src/rbac/engine.ts +38 -14
  493. package/src/rbac/hooks/permissions/index.ts +7 -0
  494. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  495. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  496. package/src/rbac/hooks/permissions/useCan.ts +347 -0
  497. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  498. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  499. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  500. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  501. package/src/rbac/hooks/useCan.test.ts +71 -64
  502. package/src/rbac/hooks/usePermissions.ts +14 -995
  503. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  504. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  505. package/src/rbac/hooks/useSecureSupabase.ts +33 -13
  506. package/src/rbac/permissions.ts +0 -30
  507. package/src/rbac/secureClient.ts +212 -61
  508. package/src/rbac/types.ts +8 -0
  509. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  510. package/src/theming/parseEventColours.ts +5 -19
  511. package/src/types/vitest-globals.d.ts +51 -26
  512. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  513. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  514. package/src/utils/__tests__/index.unit.test.ts +2 -2
  515. package/src/utils/audit/audit.ts +0 -3
  516. package/src/utils/core/cn.ts +1 -1
  517. package/src/utils/file-reference/index.ts +53 -1
  518. package/src/utils/formatting/formatting.ts +8 -18
  519. package/src/utils/index.ts +0 -1
  520. package/src/utils/security/secureDataAccess.test.ts +31 -20
  521. package/src/utils/security/secureDataAccess.ts +4 -3
  522. package/dist/chunk-6C4YBBJM.js +0 -628
  523. package/dist/chunk-6C4YBBJM.js.map +0 -1
  524. package/dist/chunk-7D4SUZUM.js 2.map +0 -1
  525. package/dist/chunk-7EQTDTTJ.js 2.map +0 -1
  526. package/dist/chunk-7EQTDTTJ.js.map +0 -1
  527. package/dist/chunk-7FLMSG37.js 2.map +0 -1
  528. package/dist/chunk-7FLMSG37.js.map +0 -1
  529. package/dist/chunk-BC4IJKSL.js.map +0 -1
  530. package/dist/chunk-E3SPN4VZ.js +0 -12917
  531. package/dist/chunk-E3SPN4VZ.js.map +0 -1
  532. package/dist/chunk-E66EQZE6 5.js +0 -37
  533. package/dist/chunk-E66EQZE6.js 2.map +0 -1
  534. package/dist/chunk-HWIIPPNI.js.map +0 -1
  535. package/dist/chunk-I7PSE6JW 5.js +0 -191
  536. package/dist/chunk-I7PSE6JW.js 2.map +0 -1
  537. package/dist/chunk-I7PSE6JW.js.map +0 -1
  538. package/dist/chunk-IIELH4DL.js.map +0 -1
  539. package/dist/chunk-KNC55RTG.js 5.map +0 -1
  540. package/dist/chunk-KNC55RTG.js.map +0 -1
  541. package/dist/chunk-KQCRWDSA.js 5.map +0 -1
  542. package/dist/chunk-LFNCN2SP.js +0 -412
  543. package/dist/chunk-LFNCN2SP.js 2.map +0 -1
  544. package/dist/chunk-LFNCN2SP.js.map +0 -1
  545. package/dist/chunk-LMC26NLJ 2.js +0 -84
  546. package/dist/chunk-NOAYCWCX.js +0 -4993
  547. package/dist/chunk-NOAYCWCX.js.map +0 -1
  548. package/dist/chunk-QWWZ5CAQ.js 3.map +0 -1
  549. package/dist/chunk-QWWZ5CAQ.js.map +0 -1
  550. package/dist/chunk-QXHPKYJV 3.js +0 -113
  551. package/dist/chunk-R77UEZ4E.js +0 -68
  552. package/dist/chunk-R77UEZ4E.js.map +0 -1
  553. package/dist/chunk-SQGMNID3.js.map +0 -1
  554. package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
  555. package/dist/chunk-XNXXZ43G.js.map +0 -1
  556. package/dist/chunk-ZSAAAMVR 6.js +0 -25
  557. package/dist/components.js 5.map +0 -1
  558. package/dist/styles/index 2.js +0 -12
  559. package/dist/styles/index.js 5.map +0 -1
  560. package/dist/theming/runtime 5.js +0 -19
  561. package/dist/theming/runtime.js 5.map +0 -1
  562. package/docs/api/classes/ErrorBoundary.md +0 -144
  563. package/docs/migration/quick-migration-guide.md +0 -356
  564. package/docs/migration/service-architecture.md +0 -281
  565. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  566. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  567. package/src/hooks/useSecureDataAccess.ts +0 -666
  568. /package/dist/{DataTable-5FU7IESH.js.map → DataTable-TPTKCX4D.js.map} +0 -0
  569. /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
  570. /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
  571. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  572. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  573. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  574. /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
  575. /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
  576. /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
  577. /package/examples/{rbac → RBAC}/index.ts +0 -0
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  formatInTimeZone,
3
3
  getTimezoneAbbreviation
4
- } from "./chunk-HW3OVDUF.js";
4
+ } from "./chunk-J36DSWQK.js";
5
5
  import {
6
6
  createLogger
7
7
  } from "./chunk-PWLANIRT.js";
@@ -240,23 +240,21 @@ function formatCurrency(value, currencyCode = "USD", locale = "en-US") {
240
240
  function formatNumber(value, options = {}, locale = "en-US") {
241
241
  return new Intl.NumberFormat(locale, options).format(value);
242
242
  }
243
- function formatPercent(value, locale = "en-US", decimalsOrOptions) {
243
+ function formatPercent(value, locale = "en-US", options) {
244
244
  let decimals;
245
- if (typeof decimalsOrOptions === "number") {
246
- decimals = decimalsOrOptions;
247
- } else if (decimalsOrOptions && typeof decimalsOrOptions === "object") {
248
- if (decimalsOrOptions.preserveDecimals) {
245
+ if (options && typeof options === "object") {
246
+ if (options.preserveDecimals) {
249
247
  const valueStr = value.toString();
250
248
  const decimalIndex = valueStr.indexOf(".");
251
249
  if (decimalIndex !== -1) {
252
250
  const detectedDecimals = valueStr.length - decimalIndex - 1;
253
- const maxDecimals = decimalsOrOptions.maxDecimals ?? 10;
251
+ const maxDecimals = options.maxDecimals ?? 10;
254
252
  decimals = Math.min(detectedDecimals, maxDecimals);
255
253
  } else {
256
254
  decimals = 0;
257
255
  }
258
256
  } else {
259
- decimals = decimalsOrOptions.decimals ?? 1;
257
+ decimals = options.decimals ?? 1;
260
258
  }
261
259
  } else {
262
260
  decimals = 1;
@@ -385,4 +383,4 @@ export {
385
383
  formatDateTimeForTable,
386
384
  formatDateTimeForMap
387
385
  };
388
- //# sourceMappingURL=chunk-QWWZ5CAQ.js.map
386
+ //# sourceMappingURL=chunk-FFQEQTNW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/context/sessionTracking.ts","../src/utils/validation/common.ts","../src/utils/validation/passwordSchema.ts","../src/utils/app/appConfig.ts","../src/utils/formatting/formatting.ts"],"sourcesContent":["import type { SupabaseClient } from '@supabase/supabase-js';\nimport { createLogger } from '../core/logger';\n\nconst log = createLogger('SessionTracking');\n\n// Define the tracking parameters locally since old RBAC types are removed\ninterface TrackUserSessionParams {\n p_session_type: 'event_switch' | 'session_expired';\n p_event_id?: string;\n p_app_id?: string;\n ip_address?: string;\n user_agent?: string;\n}\n\n/**\n * Hook for manual session tracking (event switches and session expiration).\n * \n * Note: Login and logout tracking is automatically handled by UnifiedAuthProvider.\n * You should only use this hook for tracking event switches or session expirations.\n * \n * @param supabaseClient - Supabase client instance\n * @param appName - Optional application name for tracking\n * @returns Object containing tracking functions for event switches and session expiration\n */\nexport function useSessionTracking(supabaseClient: SupabaseClient, appName?: string) {\n // Resolve app name to app_id\n const resolveAppId = async (): Promise<string | undefined> => {\n if (!appName) return undefined;\n \n try {\n const { data, error } = await supabaseClient\n .from('rbac_apps')\n .select('id')\n .eq('name', appName)\n .eq('is_active', true)\n .single();\n \n if (error || !data) {\n log.warn('App not found or inactive:', appName);\n return undefined;\n }\n \n return data.id;\n } catch (error) {\n log.error('Failed to resolve app ID:', error);\n return undefined;\n }\n };\n /**\n * Track an event switch\n * @param eventId - ID of the event being switched to\n */\n const trackEventSwitch = async (eventId: string) => {\n try {\n const { data: { user } } = await supabaseClient.auth.getUser();\n if (!user) {\n log.warn('No authenticated user found for session tracking');\n return;\n }\n\n const appId = await resolveAppId();\n\n const params: TrackUserSessionParams = {\n p_session_type: 'event_switch',\n p_event_id: eventId,\n p_app_id: appId\n };\n\n const { error } = await supabaseClient.rpc('rbac_session_track', {\n p_user_id: user?.id,\n p_session_type: params.p_session_type,\n p_event_id: params.p_event_id,\n p_app_id: params.p_app_id,\n p_ip_address: params.ip_address,\n p_user_agent: params.user_agent\n });\n \n if (error) {\n log.error('Failed to track event switch session:', error);\n }\n } catch (error) {\n log.error('Failed to track event switch:', error);\n }\n };\n\n /**\n * Track a session expiration\n */\n const trackSessionExpired = async () => {\n try {\n const { data: { user } } = await supabaseClient.auth.getUser();\n if (!user) {\n log.warn('No authenticated user found for session tracking');\n return;\n }\n\n const appId = await resolveAppId();\n\n const params: TrackUserSessionParams = {\n p_session_type: 'session_expired',\n p_app_id: appId\n };\n\n const { error } = await supabaseClient.rpc('rbac_session_track', {\n p_user_id: user?.id,\n p_session_type: params.p_session_type,\n p_event_id: params.p_event_id,\n p_app_id: params.p_app_id,\n p_ip_address: params.ip_address,\n p_user_agent: params.user_agent\n });\n \n if (error) {\n log.error('Failed to track session expiration:', error);\n }\n } catch (error) {\n log.error('Failed to track session expiration:', error);\n }\n };\n\n return {\n trackEventSwitch,\n trackSessionExpired\n };\n} ","\n/**\n * @file Common validation schemas\n * @description Reusable validation schemas for common data types\n */\n\nimport { z } from 'zod';\n\n/**\n * Email validation schema\n */\nexport const emailSchema = z\n .string()\n .min(1, 'Email is required')\n .email('Invalid email format')\n .max(254, 'Email too long');\n\n/**\n * Name validation schema\n */\nexport const nameSchema = z\n .string()\n .min(1, 'Name is required')\n .max(100, 'Name too long')\n .regex(/^[a-zA-Z\\s'-]+$/, 'Name contains invalid characters');\n\n/**\n * Phone number validation schema\n */\nexport const phoneSchema = z\n .string()\n .regex(/^\\+?[\\d\\s\\-\\(\\)]+$/, 'Invalid phone number format')\n .min(10, 'Phone number too short')\n .max(20, 'Phone number too long');\n\n/**\n * URL validation schema\n */\nexport const urlSchema = z\n .string()\n .url('Invalid URL format')\n .max(2048, 'URL too long');\n\n/**\n * Date validation schema\n */\nexport const dateSchema = z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/, 'Date must be in YYYY-MM-DD format')\n .refine((date) => {\n const parsed = new Date(date);\n return !isNaN(parsed.getTime());\n }, 'Invalid date');\n","\n/**\n * @file Enhanced Password Schema with Security Validations\n * @description Comprehensive password validation with security checks\n */\n\nimport { z } from 'zod';\n\n// Common weak passwords to check against\nconst COMMON_PASSWORDS = new Set([\n 'password', '123456', '123456789', 'qwerty', 'abc123', 'password123',\n 'admin', 'letmein', 'welcome', 'monkey', '1234567890', 'password1'\n]);\n\n// Common password patterns to avoid\nconst WEAK_PATTERNS = [\n /^(.)\\1+$/, // All same character\n /^(012|123|234|345|456|567|678|789|890|987|876|765|654|543|432|321|210)+/, // Sequential numbers\n /^(abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz)+/i, // Sequential letters\n];\n\n/**\n * Enhanced password validation schema with security checks\n */\nexport const securePasswordSchema = z\n .string()\n .min(8, 'Password must be at least 8 characters long')\n .max(128, 'Password must not exceed 128 characters')\n .refine(\n (password) => /[a-z]/.test(password),\n 'Password must contain at least one lowercase letter'\n )\n .refine(\n (password) => /[A-Z]/.test(password),\n 'Password must contain at least one uppercase letter'\n )\n .refine(\n (password) => /\\d/.test(password),\n 'Password must contain at least one number'\n )\n .refine(\n (password) => /[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?]/.test(password),\n 'Password must contain at least one special character'\n )\n .refine(\n (password) => !COMMON_PASSWORDS.has(password.toLowerCase()),\n 'Password is too common. Please choose a stronger password'\n )\n .refine(\n (password) => !WEAK_PATTERNS.some(pattern => pattern.test(password)),\n 'Password contains weak patterns. Please choose a more complex password'\n )\n .refine(\n (password) => {\n // Check for keyboard patterns (qwerty, asdf, etc.)\n const keyboardPatterns = ['qwerty', 'asdfgh', 'zxcvbn', '1234567890'];\n return !keyboardPatterns.some(pattern => \n password.toLowerCase().includes(pattern)\n );\n },\n 'Password contains keyboard patterns. Please choose a more secure password'\n );\n\n/**\n * Basic password schema for less strict requirements\n */\nexport const passwordSchema = z\n .string()\n .min(6, 'Password must be at least 6 characters long')\n .max(128, 'Password must not exceed 128 characters');\n\n/**\n * Password strength calculator\n */\nexport function calculatePasswordStrength(password: string): {\n score: number;\n feedback: string[];\n level: 'very-weak' | 'weak' | 'fair' | 'good' | 'strong';\n} {\n let score = 0;\n const feedback: string[] = [];\n\n // Length check\n if (password.length >= 8) score += 20;\n else if (password.length >= 6) score += 10;\n else feedback.push('Use at least 8 characters');\n\n // Character variety\n if (/[a-z]/.test(password)) score += 15;\n else feedback.push('Add lowercase letters');\n\n if (/[A-Z]/.test(password)) score += 15;\n else feedback.push('Add uppercase letters');\n\n if (/\\d/.test(password)) score += 15;\n else feedback.push('Add numbers');\n\n if (/[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?]/.test(password)) score += 15;\n else feedback.push('Add special characters');\n\n // Additional complexity\n if (password.length >= 12) score += 10;\n if (/[^a-zA-Z0-9]/.test(password)) score += 10;\n\n // Penalties\n if (COMMON_PASSWORDS.has(password.toLowerCase())) {\n score -= 30;\n feedback.push('Avoid common passwords');\n }\n\n if (WEAK_PATTERNS.some(pattern => pattern.test(password))) {\n score -= 20;\n feedback.push('Avoid predictable patterns');\n }\n\n // Determine level\n let level: 'very-weak' | 'weak' | 'fair' | 'good' | 'strong';\n if (score < 30) level = 'very-weak';\n else if (score < 50) level = 'weak';\n else if (score < 70) level = 'fair';\n else if (score < 90) level = 'good';\n else level = 'strong';\n\n return { score: Math.max(0, Math.min(100, score)), feedback, level };\n}\n","\n/**\n * Application configuration utilities\n */\n\nexport interface AppConfig {\n appName: string;\n appId: string;\n}\n\nlet currentAppConfig: AppConfig | null = null;\n\n/**\n * Set the current application configuration\n */\nexport function setAppConfig(config: AppConfig) {\n currentAppConfig = config;\n}\n\n/**\n * Get the current application configuration\n */\nexport function getAppConfig(): AppConfig {\n if (!currentAppConfig) {\n // Fallback to environment or default\n const appName = import.meta.env.REACT_APP_NAME || 'PACE';\n return {\n appName,\n appId: appName\n };\n }\n return currentAppConfig;\n}\n\n/**\n * Get the current app name\n */\nexport function getCurrentAppName(): string {\n return getAppConfig().appName;\n}\n\n/**\n * Get the current app ID\n */\nexport function getCurrentAppId(): string {\n return getAppConfig().appId;\n}\n","/**\n * Utility functions for formatting data in the application\n */\n\nimport { parseISO, isValid } from 'date-fns';\nimport { formatInTimeZone, getTimezoneAbbreviation } from '../timezone';\n\n/**\n * Format a date as a readable string in \"dd mmm yyyy\" format (e.g., \"15 Jun 2024\")\n */\nexport function formatDate(date: Date | string | number): string {\n const dateObj = typeof date === 'string' || typeof date === 'number' \n ? new Date(date) \n : date;\n \n // Use 'en-GB' locale to ensure \"dd mmm yyyy\" format (e.g., \"15 Jun 2024\")\n return dateObj.toLocaleDateString('en-GB', {\n year: 'numeric',\n month: 'short',\n day: 'numeric'\n });\n}\n\n/**\n * Format a time as a readable string in \"HH:mm\" format (e.g., \"14:30\")\n * Uses 24-hour format for consistency across pace apps\n */\nexport function formatTime(date: Date | string | number): string {\n const dateObj = typeof date === 'string' || typeof date === 'number' \n ? new Date(date) \n : date;\n \n // Use 'en-GB' locale to ensure \"HH:mm\" format (24-hour format)\n return dateObj.toLocaleTimeString('en-GB', {\n hour: '2-digit',\n minute: '2-digit',\n hour12: false\n });\n}\n\n/**\n * Format a date and time as a readable string in \"dd mmm yyyy, HH:mm\" format (e.g., \"15 Jun 2024, 14:30\")\n * Uses 24-hour format for consistency across pace apps\n */\nexport function formatDateTime(date: Date | string | number): string {\n const dateObj = typeof date === 'string' || typeof date === 'number' \n ? new Date(date) \n : date;\n \n // Use 'en-GB' locale to ensure consistent format (e.g., \"15 Jun 2024, 14:30\")\n return dateObj.toLocaleString('en-GB', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false\n });\n}\n\n/**\n * Format a number as a currency\n */\nexport function formatCurrency(value: number, currencyCode = 'USD', locale = 'en-US'): string {\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: currencyCode,\n }).format(value);\n}\n\n/**\n * Format a number with custom options\n */\nexport function formatNumber(\n value: number,\n options: Intl.NumberFormatOptions = {},\n locale = 'en-US'\n): string {\n return new Intl.NumberFormat(locale, options).format(value);\n}\n\n/**\n * Format a number as a percentage.\n * \n * @param value - The percentage value as a decimal (e.g., 0.81 for 0.81%)\n * @param locale - The locale string (default: 'en-US')\n * @param options - Options object with:\n * - `decimals` - Fixed number of decimal places (default: 1)\n * - `preserveDecimals` - Auto-detect and preserve decimal places from the input value\n * - `maxDecimals` - Maximum decimal places when preserving (default: 10)\n * @returns Formatted percentage string (e.g., \"0.81%\", \"81%\")\n * \n * @example\n * ```ts\n * // Fixed decimals (default behavior)\n * formatPercent(0.5) // '0.5%'\n * formatPercent(0.81, 'en-US', { decimals: 1 }) // '0.8%' (loses precision)\n * \n * // Preserve decimal places dynamically\n * formatPercent(0.81, 'en-US', { preserveDecimals: true }) // '0.81%'\n * formatPercent(0.8123, 'en-US', { preserveDecimals: true, maxDecimals: 2 }) // '0.81%'\n * ```\n */\nexport function formatPercent(\n value: number,\n locale: string = 'en-US',\n options?: {\n decimals?: number;\n preserveDecimals?: boolean;\n maxDecimals?: number;\n }\n): string {\n let decimals: number;\n\n if (options && typeof options === 'object') {\n // Check if we should preserve decimals\n if (options.preserveDecimals) {\n const valueStr = value.toString();\n const decimalIndex = valueStr.indexOf('.');\n \n if (decimalIndex !== -1) {\n const detectedDecimals = valueStr.length - decimalIndex - 1;\n const maxDecimals = options.maxDecimals ?? 10;\n decimals = Math.min(detectedDecimals, maxDecimals);\n } else {\n decimals = 0;\n }\n } else {\n decimals = options.decimals ?? 1;\n }\n } else {\n decimals = 1;\n }\n\n return new Intl.NumberFormat(locale, {\n style: 'percent',\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n }).format(value / 100);\n}\n\n/**\n * Format a large number with abbreviations (K, M, B)\n */\nexport function formatCompactNumber(value: number, locale = 'en-US'): string {\n return new Intl.NumberFormat(locale, {\n notation: 'compact',\n compactDisplay: 'short'\n }).format(value);\n}\n\n/**\n * Format a file size in bytes to a human-readable string\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 Bytes';\n \n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n \n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];\n}\n\n/**\n * Options for formatting date/time with timezone\n */\nexport interface DateTimeFormatOptions {\n /**\n * Include timezone abbreviation (default: true)\n */\n includeTimezone?: boolean;\n /**\n * Custom format string (default: 'MMM dd, yyyy HH:mm')\n */\n format?: string;\n}\n\n/**\n * Format a UTC date for display in a specific timezone\n *\n * @param utcDate - UTC date (ISO string, Date object, or undefined)\n * @param timezone - IANA timezone string (e.g., 'America/New_York')\n * @param options - Formatting options\n * @returns Formatted date string or empty string if invalid\n *\n * @example\n * ```ts\n * formatDateTimeForDisplay('2024-01-15T10:00:00Z', 'America/New_York');\n * // \"Jan 15, 2024 05:00 (EST)\"\n *\n * formatDateTimeForDisplay('2024-01-15T10:00:00Z', 'America/New_York', { includeTimezone: false });\n * // \"Jan 15, 2024 05:00\"\n * ```\n */\nexport function formatDateTimeForDisplay(\n utcDate: string | Date | undefined,\n timezone: string | undefined,\n options: DateTimeFormatOptions = {}\n): string {\n if (!utcDate) {\n return '';\n }\n\n if (!timezone) {\n return '';\n }\n\n try {\n const { includeTimezone = true, format: formatStr = 'MMM dd, yyyy HH:mm' } = options;\n\n let dateObj: Date;\n if (typeof utcDate === 'string') {\n dateObj = parseISO(utcDate);\n } else {\n dateObj = utcDate;\n }\n\n if (!isValid(dateObj)) {\n return '';\n }\n\n const formatted = formatInTimeZone(dateObj, timezone, formatStr);\n\n if (includeTimezone) {\n const tzAbbr = getTimezoneAbbreviation(dateObj, timezone);\n return `${formatted} (${tzAbbr})`;\n }\n\n return formatted;\n } catch {\n return '';\n }\n}\n\n/**\n * Format a UTC date for display (date only, no time)\n *\n * @param utcDate - UTC date (ISO string, Date object, or undefined)\n * @returns Formatted date string or empty string if invalid\n *\n * @example\n * ```ts\n * formatDateOnlyForDisplay('2024-01-15T10:00:00Z');\n * // \"15 January 2024\"\n * ```\n */\nexport function formatDateOnlyForDisplay(utcDate: string | Date | undefined): string {\n if (!utcDate) {\n return '';\n }\n\n try {\n let dateObj: Date;\n if (typeof utcDate === 'string') {\n dateObj = parseISO(utcDate);\n } else {\n dateObj = utcDate;\n }\n\n if (!isValid(dateObj)) {\n return '';\n }\n\n // Use 'en-GB' locale for \"dd mmm yyyy\" format\n return dateObj.toLocaleDateString('en-GB', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n });\n } catch {\n return '';\n }\n}\n\n/**\n * Format a UTC date for table display (compact format with timezone)\n *\n * @param utcDate - UTC date (ISO string, Date object, or undefined)\n * @param timezone - IANA timezone string\n * @returns Formatted date string or empty string if invalid\n *\n * @example\n * ```ts\n * formatDateTimeForTable('2024-01-15T10:00:00Z', 'America/New_York');\n * // \"Jan 15, 2024 05:00 (EST)\"\n * ```\n */\nexport function formatDateTimeForTable(\n utcDate: string | Date | undefined,\n timezone: string | undefined\n): string {\n return formatDateTimeForDisplay(utcDate, timezone, {\n includeTimezone: true,\n format: 'MMM dd, yyyy HH:mm'\n });\n}\n\n/**\n * Format a UTC date for map display (compact format)\n *\n * @param utcDate - UTC date (ISO string, Date object, or undefined)\n * @param timezone - IANA timezone string\n * @returns Formatted date string or empty string if invalid\n *\n * @example\n * ```ts\n * formatDateTimeForMap('2024-01-15T10:00:00Z', 'America/New_York');\n * // \"Jan 15, 05:00 EST\"\n * ```\n */\nexport function formatDateTimeForMap(\n utcDate: string | Date | undefined,\n timezone: string | undefined\n): string {\n if (!utcDate || !timezone) {\n return '';\n }\n\n try {\n let dateObj: Date;\n if (typeof utcDate === 'string') {\n dateObj = parseISO(utcDate);\n } else {\n dateObj = utcDate;\n }\n\n if (!isValid(dateObj)) {\n return '';\n }\n\n const formatted = formatInTimeZone(dateObj, timezone, 'MMM dd, HH:mm');\n const tzAbbr = getTimezoneAbbreviation(dateObj, timezone);\n\n return `${formatted} ${tzAbbr}`;\n } catch {\n return '';\n }\n}\n"],"mappings":"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","names":["z"]}
@@ -0,0 +1,76 @@
1
+ // src/utils/performance/performanceBudgets.ts
2
+ var PERFORMANCE_BUDGETS = {
3
+ COMPONENT_RENDER: { threshold: 50 },
4
+ BUNDLE_SIZE: { threshold: 15e4 },
5
+ CHUNK_COUNT: { threshold: 10 },
6
+ TREESHAKING_SCORE: { threshold: 70 },
7
+ ERROR_BOUNDARY_TRIGGER: { threshold: 5 },
8
+ MEMORY_INCREASE: { threshold: 1e3 },
9
+ LARGE_LIST_RENDER: { threshold: 500 }
10
+ };
11
+ var PerformanceBudgetMonitor = class {
12
+ constructor() {
13
+ this.metrics = /* @__PURE__ */ new Map();
14
+ this.budgets = [];
15
+ }
16
+ measure(metric, value, metadata) {
17
+ this.metrics.set(metric, value);
18
+ if (import.meta.env.MODE === "development" || import.meta.env.MODE === "test" || false) {
19
+ console.log("\u{1F4CA} Performance Metric: " + metric + " = " + value, metadata);
20
+ }
21
+ const budgetConfig = PERFORMANCE_BUDGETS[metric];
22
+ const threshold = budgetConfig?.threshold || 100;
23
+ const passed = value <= threshold;
24
+ return {
25
+ passed,
26
+ value,
27
+ threshold
28
+ };
29
+ }
30
+ setBudget(metric, budget, threshold = "warning") {
31
+ this.budgets.push({ metric, budget, actual: 0, threshold });
32
+ }
33
+ checkBudgets() {
34
+ const violations = [];
35
+ for (const budget of this.budgets) {
36
+ const actual = this.metrics.get(budget.metric) || 0;
37
+ budget.actual = actual;
38
+ if (actual > budget.budget) {
39
+ violations.push(budget);
40
+ if (import.meta.env.MODE === "development" || import.meta.env.MODE === "test" || false) {
41
+ if (budget.threshold === "error") {
42
+ console.error("\u274C Performance budget exceeded: " + budget.metric + " (" + actual + " > " + budget.budget + ")");
43
+ } else if (budget.threshold === "warning") {
44
+ console.warn("\u26A0\uFE0F Performance budget exceeded: " + budget.metric + " (" + actual + " > " + budget.budget + ")");
45
+ } else {
46
+ console.info("\u2139\uFE0F Performance budget exceeded: " + budget.metric + " (" + actual + " > " + budget.budget + ")");
47
+ }
48
+ }
49
+ }
50
+ }
51
+ return violations;
52
+ }
53
+ getMetrics() {
54
+ return {
55
+ bundleSize: this.metrics.get("BUNDLE_SIZE") || 0,
56
+ chunkCount: this.metrics.get("CHUNK_COUNT") || 0,
57
+ treeshakingEffectiveness: this.metrics.get("TREESHAKING_SCORE") || 0,
58
+ dynamicImportUsage: this.metrics.get("DYNAMIC_IMPORTS") || 0
59
+ };
60
+ }
61
+ reset() {
62
+ this.metrics.clear();
63
+ this.budgets.length = 0;
64
+ }
65
+ };
66
+ var performanceBudgetMonitor = new PerformanceBudgetMonitor();
67
+ performanceBudgetMonitor.setBudget("BUNDLE_SIZE", 15e4, "error");
68
+ performanceBudgetMonitor.setBudget("CHUNK_COUNT", 10, "warning");
69
+ performanceBudgetMonitor.setBudget("TREESHAKING_SCORE", 70, "warning");
70
+ performanceBudgetMonitor.setBudget("ERROR_BOUNDARY_TRIGGER", 5, "error");
71
+
72
+ export {
73
+ PERFORMANCE_BUDGETS,
74
+ performanceBudgetMonitor
75
+ };
76
+ //# sourceMappingURL=chunk-FMUCXFII.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/performance/performanceBudgets.ts"],"sourcesContent":["\ninterface PerformanceBudget {\n metric: string;\n budget: number;\n actual: number;\n threshold: 'error' | 'warning' | 'info';\n}\n\ninterface PerformanceMetrics {\n bundleSize: number;\n chunkCount: number;\n treeshakingEffectiveness: number;\n dynamicImportUsage: number;\n}\n\ninterface MeasurementResult {\n passed: boolean;\n value: number;\n threshold: number;\n}\n\n// Performance budget thresholds with index signature\nexport const PERFORMANCE_BUDGETS: { [key: string]: { threshold: number } } = {\n COMPONENT_RENDER: { threshold: 50 },\n BUNDLE_SIZE: { threshold: 150000 },\n CHUNK_COUNT: { threshold: 10 },\n TREESHAKING_SCORE: { threshold: 70 },\n ERROR_BOUNDARY_TRIGGER: { threshold: 5 },\n MEMORY_INCREASE: { threshold: 1000 },\n LARGE_LIST_RENDER: { threshold: 500 },\n} as const;\n\nclass PerformanceBudgetMonitor {\n private metrics: Map<string, number> = new Map();\n private budgets: PerformanceBudget[] = [];\n\n measure(metric: string, value: number, metadata?: Record<string, unknown>): MeasurementResult {\n this.metrics.set(metric, value);\n \n // In production, this would send to a proper logging service\n // For now, we'll log to console for testing purposes\n if (import.meta.env.MODE === 'development' || import.meta.env.MODE === 'test' || process.env.NODE_ENV === 'test') {\n console.log('📊 Performance Metric: ' + metric + ' = ' + value, metadata);\n }\n\n // Get threshold for this metric\n const budgetConfig = PERFORMANCE_BUDGETS[metric];\n const threshold = budgetConfig?.threshold || 100;\n const passed = value <= threshold;\n\n return {\n passed,\n value,\n threshold\n };\n }\n\n setBudget(metric: string, budget: number, threshold: 'error' | 'warning' | 'info' = 'warning'): void {\n this.budgets.push({ metric, budget, actual: 0, threshold });\n }\n\n checkBudgets(): PerformanceBudget[] {\n const violations: PerformanceBudget[] = [];\n \n for (const budget of this.budgets) {\n const actual = this.metrics.get(budget.metric) || 0;\n budget.actual = actual;\n \n if (actual > budget.budget) {\n violations.push(budget);\n \n // In production, this would send to a proper logging service\n // For now, we'll log to console for testing purposes\n if (import.meta.env.MODE === 'development' || import.meta.env.MODE === 'test' || process.env.NODE_ENV === 'test') {\n if (budget.threshold === 'error') {\n console.error('❌ Performance budget exceeded: ' + budget.metric + ' (' + actual + ' > ' + budget.budget + ')');\n } else if (budget.threshold === 'warning') {\n console.warn('⚠️ Performance budget exceeded: ' + budget.metric + ' (' + actual + ' > ' + budget.budget + ')');\n } else {\n console.info('ℹ️ Performance budget exceeded: ' + budget.metric + ' (' + actual + ' > ' + budget.budget + ')');\n }\n }\n }\n }\n \n return violations;\n }\n\n getMetrics(): PerformanceMetrics {\n return {\n bundleSize: this.metrics.get('BUNDLE_SIZE') || 0,\n chunkCount: this.metrics.get('CHUNK_COUNT') || 0,\n treeshakingEffectiveness: this.metrics.get('TREESHAKING_SCORE') || 0,\n dynamicImportUsage: this.metrics.get('DYNAMIC_IMPORTS') || 0,\n };\n }\n\n reset(): void {\n this.metrics.clear();\n this.budgets.length = 0;\n }\n}\n\nexport const performanceBudgetMonitor = new PerformanceBudgetMonitor();\n\n// Set default performance budgets\nperformanceBudgetMonitor.setBudget('BUNDLE_SIZE', 150000, 'error'); // 150KB\nperformanceBudgetMonitor.setBudget('CHUNK_COUNT', 10, 'warning');\nperformanceBudgetMonitor.setBudget('TREESHAKING_SCORE', 70, 'warning');\nperformanceBudgetMonitor.setBudget('ERROR_BOUNDARY_TRIGGER', 5, 'error');\n"],"mappings":";AAsBO,IAAM,sBAAgE;AAAA,EAC3E,kBAAkB,EAAE,WAAW,GAAG;AAAA,EAClC,aAAa,EAAE,WAAW,KAAO;AAAA,EACjC,aAAa,EAAE,WAAW,GAAG;AAAA,EAC7B,mBAAmB,EAAE,WAAW,GAAG;AAAA,EACnC,wBAAwB,EAAE,WAAW,EAAE;AAAA,EACvC,iBAAiB,EAAE,WAAW,IAAK;AAAA,EACnC,mBAAmB,EAAE,WAAW,IAAI;AACtC;AAEA,IAAM,2BAAN,MAA+B;AAAA,EAA/B;AACE,SAAQ,UAA+B,oBAAI,IAAI;AAC/C,SAAQ,UAA+B,CAAC;AAAA;AAAA,EAExC,QAAQ,QAAgB,OAAe,UAAuD;AAC5F,SAAK,QAAQ,IAAI,QAAQ,KAAK;AAI9B,QAAI,YAAY,IAAI,SAAS,iBAAiB,YAAY,IAAI,SAAS,UAAU,OAAiC;AAChH,cAAQ,IAAI,mCAA4B,SAAS,QAAQ,OAAO,QAAQ;AAAA,IAC1E;AAGA,UAAM,eAAe,oBAAoB,MAAM;AAC/C,UAAM,YAAY,cAAc,aAAa;AAC7C,UAAM,SAAS,SAAS;AAExB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,UAAU,QAAgB,QAAgB,YAA0C,WAAiB;AACnG,SAAK,QAAQ,KAAK,EAAE,QAAQ,QAAQ,QAAQ,GAAG,UAAU,CAAC;AAAA,EAC5D;AAAA,EAEA,eAAoC;AAClC,UAAM,aAAkC,CAAC;AAEzC,eAAW,UAAU,KAAK,SAAS;AACjC,YAAM,SAAS,KAAK,QAAQ,IAAI,OAAO,MAAM,KAAK;AAClD,aAAO,SAAS;AAEhB,UAAI,SAAS,OAAO,QAAQ;AAC1B,mBAAW,KAAK,MAAM;AAItB,YAAI,YAAY,IAAI,SAAS,iBAAiB,YAAY,IAAI,SAAS,UAAU,OAAiC;AAChH,cAAI,OAAO,cAAc,SAAS;AAChC,oBAAQ,MAAM,yCAAoC,OAAO,SAAS,OAAO,SAAS,QAAQ,OAAO,SAAS,GAAG;AAAA,UAC/G,WAAW,OAAO,cAAc,WAAW;AACzC,oBAAQ,KAAK,+CAAqC,OAAO,SAAS,OAAO,SAAS,QAAQ,OAAO,SAAS,GAAG;AAAA,UAC/G,OAAO;AACL,oBAAQ,KAAK,+CAAqC,OAAO,SAAS,OAAO,SAAS,QAAQ,OAAO,SAAS,GAAG;AAAA,UAC/G;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,aAAiC;AAC/B,WAAO;AAAA,MACL,YAAY,KAAK,QAAQ,IAAI,aAAa,KAAK;AAAA,MAC/C,YAAY,KAAK,QAAQ,IAAI,aAAa,KAAK;AAAA,MAC/C,0BAA0B,KAAK,QAAQ,IAAI,mBAAmB,KAAK;AAAA,MACnE,oBAAoB,KAAK,QAAQ,IAAI,iBAAiB,KAAK;AAAA,IAC7D;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,SAAK,QAAQ,MAAM;AACnB,SAAK,QAAQ,SAAS;AAAA,EACxB;AACF;AAEO,IAAM,2BAA2B,IAAI,yBAAyB;AAGrE,yBAAyB,UAAU,eAAe,MAAQ,OAAO;AACjE,yBAAyB,UAAU,eAAe,IAAI,SAAS;AAC/D,yBAAyB,UAAU,qBAAqB,IAAI,SAAS;AACrE,yBAAyB,UAAU,0BAA0B,GAAG,OAAO;","names":[]}
@@ -212,4 +212,4 @@ export {
212
212
  CachedAppIdResolver,
213
213
  cachedAppIdResolver
214
214
  };
215
- //# sourceMappingURL=chunk-HW3OVDUF.js.map
215
+ //# sourceMappingURL=chunk-J36DSWQK.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/LoadingSpinner/LoadingSpinner.tsx","../src/utils/timezone/timezone.ts","../src/utils/app/appIdResolver.ts"],"sourcesContent":["/**\n * @file LoadingSpinner Component\n * @package @jmruthers/pace-core\n * @module Components/LoadingSpinner\n * @since 0.1.0\n *\n * A simple, accessible loading spinner component for indicating loading states.\n * Provides smooth animations with reduced motion support for accessibility.\n *\n * Features:\n * - Multiple size variants (sm, md, lg)\n * - Smooth CSS animations\n * - Reduced motion support for accessibility\n * - Screen reader friendly with proper ARIA attributes\n * - Customizable styling\n * - Lightweight and performant\n *\n * @example\n * ```tsx\n * // Basic loading spinner\n * <LoadingSpinner />\n * \n * // Different sizes\n * <LoadingSpinner size=\"sm\" />\n * <LoadingSpinner size=\"md\" />\n * <LoadingSpinner size=\"lg\" />\n * \n * // With custom styling\n * <LoadingSpinner \n * size=\"lg\" \n * className=\"text-main-500\" \n * />\n * \n * // In a button\n * <Button disabled>\n * <LoadingSpinner size=\"sm\" className=\"mr-2\" />\n * Loading...\n * </Button>\n * ```\n *\n * @accessibility\n * - WCAG 2.1 AA compliant\n * - Proper ARIA role=\"status\"\n * - Screen reader announcement with \"Loading...\" text\n * - Reduced motion support for users with vestibular disorders\n * - High contrast support\n *\n * @performance\n * - CSS-only animations for optimal performance\n * - No JavaScript dependencies\n * - Minimal DOM structure\n * - Efficient rendering\n *\n * @dependencies\n * - React 18+ - Component framework\n * - Tailwind CSS - Styling and animations\n */\n\nimport React from 'react';\n\n/**\n * Props for the LoadingSpinner component\n */\nexport interface LoadingSpinnerProps {\n /** Size variant of the spinner */\n size?: 'sm' | 'md' | 'lg';\n /** Additional CSS classes for styling */\n className?: string;\n}\n\n/**\n * LoadingSpinner component\n * A simple, accessible loading spinner for indicating loading states\n * \n * @param props - Spinner configuration and styling\n * @returns JSX.Element - The rendered loading spinner\n * \n * @example\n * ```tsx\n * <LoadingSpinner size=\"lg\" className=\"text-main-500\" />\n * ```\n */\nexport const LoadingSpinner: React.FC<LoadingSpinnerProps> = ({ \n size = 'md', \n className = '' \n}) => {\n const sizeClasses: Record<'sm' | 'md' | 'lg', string> = {\n sm: 'size-4',\n md: 'size-6',\n lg: 'size-8'\n };\n\n // Ensure we always have a valid size class, defaulting to 'md' if invalid\n const validSize = size && size in sizeClasses ? size : 'md';\n const sizeClass = sizeClasses[validSize];\n\n return (\n <canvas className={`inline-block animate-spin rounded-full border-2 border-solid border-current border-r-transparent motion-reduce:animate-[spin_1.5s_linear_infinite] ${sizeClass} ${className}`.trim()} role=\"status\">\n <span className=\"sr-only\">Loading...</span>\n </canvas>\n );\n};\n","/**\n * @file Timezone Utilities\n * @package @jmruthers/pace-core\n * @module Utils/Timezone\n * @since 0.1.0\n *\n * Comprehensive timezone conversion and formatting utilities using date-fns-tz and native Intl APIs.\n * Provides functions for timezone-aware date operations, conversions, and formatting.\n *\n * Features:\n * - Format dates in specific timezones\n * - Get timezone abbreviations\n * - Convert between UTC and timezone local times\n * - Round dates to nearest minutes\n * - Calculate timezone differences\n * - Get user's browser timezone\n *\n * @example\n * ```ts\n * import { toZonedTime, fromZonedTime, formatInTimeZone, getUserTimeZone } from '@jmruthers/pace-core/utils/timezone';\n *\n * // Convert UTC to local timezone\n * const utcDate = new Date('2024-01-15T10:00:00Z');\n * const localDate = toZonedTime(utcDate, 'America/New_York');\n *\n * // Convert local time to UTC\n * const localInput = new Date(2024, 0, 15, 10, 0);\n * const utcDate = fromZonedTime(localInput, 'America/New_York');\n *\n * // Format with timezone\n * const formatted = formatInTimeZone(utcDate, 'America/New_York', 'MMM dd, yyyy HH:mm');\n * ```\n */\n\nimport { format, parseISO, addMinutes, differenceInHours, isValid } from 'date-fns';\nimport { formatInTimeZone as fnsFormatInTimeZone, toZonedTime as fnsToZonedTime, fromZonedTime as fnsFromZonedTime } from 'date-fns-tz';\n\n/**\n * Format a date in a specific timezone using date-fns format strings\n *\n * @param date - Date to format (Date object, ISO string, or timestamp)\n * @param timeZone - IANA timezone string (e.g., 'America/New_York')\n * @param formatStr - date-fns format string (e.g., 'MMM dd, yyyy HH:mm')\n * @returns Formatted date string or 'Invalid date' on error\n *\n * @example\n * ```ts\n * formatInTimeZone(new Date('2024-01-15T10:00:00Z'), 'America/New_York', 'MMM dd, yyyy HH:mm');\n * // \"Jan 15, 2024 05:00\"\n * ```\n */\nexport function formatInTimeZone(\n date: Date | string | number,\n timeZone: string,\n formatStr: string\n): string {\n try {\n if (!timeZone || typeof timeZone !== 'string') {\n return 'Invalid date';\n }\n\n let dateObj: Date;\n if (typeof date === 'string') {\n dateObj = parseISO(date);\n } else if (typeof date === 'number') {\n dateObj = new Date(date);\n } else {\n dateObj = date;\n }\n\n if (!isValid(dateObj)) {\n return 'Invalid date';\n }\n\n return fnsFormatInTimeZone(dateObj, timeZone, formatStr);\n } catch {\n return 'Invalid date';\n }\n}\n\n/**\n * Get the timezone abbreviation (EST, PST, etc.) for a date in a specific timezone\n *\n * @param date - Date to get abbreviation for\n * @param timeZone - IANA timezone string\n * @returns Timezone abbreviation or timezone name on error\n *\n * @example\n * ```ts\n * getTimezoneAbbreviation(new Date(), 'America/New_York');\n * // \"EST\" or \"EDT\" depending on date\n * ```\n */\nexport function getTimezoneAbbreviation(date: Date, timeZone: string): string {\n try {\n if (!timeZone || typeof timeZone !== 'string') {\n return timeZone || 'UTC';\n }\n\n if (!isValid(date)) {\n return timeZone;\n }\n\n const formatter = new Intl.DateTimeFormat('en-US', {\n timeZone,\n timeZoneName: 'short'\n });\n\n const parts = formatter.formatToParts(date);\n const timeZoneName = parts.find(part => part.type === 'timeZoneName');\n\n return timeZoneName?.value || timeZone;\n } catch {\n return timeZone || 'UTC';\n }\n}\n\n/**\n * Format time only in a specific timezone\n *\n * @param date - Date to format\n * @param timeZone - IANA timezone string\n * @returns Formatted time string (HH:mm format) or 'Invalid date' on error\n *\n * @example\n * ```ts\n * formatTimeInTimeZone(new Date('2024-01-15T10:00:00Z'), 'America/New_York');\n * // \"05:00\"\n * ```\n */\nexport function formatTimeInTimeZone(date: Date | string, timeZone: string): string {\n return formatInTimeZone(date, timeZone, 'HH:mm');\n}\n\n/**\n * Get the user's local timezone from the browser\n *\n * @returns IANA timezone string (e.g., 'America/New_York')\n *\n * @example\n * ```ts\n * const userTz = getUserTimeZone();\n * // \"America/New_York\" or user's actual timezone\n * ```\n */\nexport function getUserTimeZone(): string {\n try {\n if (typeof Intl !== 'undefined' && Intl.DateTimeFormat) {\n return Intl.DateTimeFormat().resolvedOptions().timeZone;\n }\n return 'UTC';\n } catch {\n return 'UTC';\n }\n}\n\n/**\n * Convert a UTC date to a specific timezone's local time representation\n *\n * @param date - UTC date to convert\n * @param timezone - IANA timezone string\n * @returns Date object representing local time in the timezone\n *\n * @example\n * ```ts\n * const utcDate = new Date('2024-01-15T10:00:00Z');\n * const localDate = toZonedTime(utcDate, 'America/New_York');\n * // Returns Date object representing 5:00 AM in New York\n * ```\n */\nexport function toZonedTime(date: Date, timezone: string): Date {\n try {\n if (!timezone || typeof timezone !== 'string') {\n return date;\n }\n\n if (!isValid(date)) {\n return date;\n }\n\n return fnsToZonedTime(date, timezone);\n } catch {\n return date;\n }\n}\n\n/**\n * Convert a local time in a specific timezone to UTC\n *\n * @param localDate - Local date in the timezone\n * @param timezone - IANA timezone string\n * @returns Date object in UTC\n *\n * @example\n * ```ts\n * const localDate = new Date(2024, 0, 15, 10, 0); // Jan 15, 2024 10:00 AM local\n * const utcDate = fromZonedTime(localDate, 'America/New_York');\n * // Returns Date object representing 3:00 PM UTC (if EST) or 2:00 PM UTC (if EDT)\n * ```\n */\nexport function fromZonedTime(localDate: Date, timezone: string): Date {\n try {\n if (!timezone || typeof timezone !== 'string') {\n return localDate;\n }\n\n if (!isValid(localDate)) {\n return localDate;\n }\n\n return fnsFromZonedTime(localDate, timezone);\n } catch {\n return localDate;\n }\n}\n\n/**\n * Round a date to the nearest X minutes\n *\n * @param date - Date to round\n * @param minutesStep - Number of minutes to round to (default: 5)\n * @returns Rounded date\n *\n * @example\n * ```ts\n * const date = new Date('2024-01-15T10:23:00Z');\n * roundToNearestMinutes(date, 5);\n * // Returns Date object for 10:25:00\n * ```\n */\nexport function roundToNearestMinutes(date: Date, minutesStep: number = 5): Date {\n try {\n if (!isValid(date)) {\n return date;\n }\n\n if (minutesStep <= 0 || !Number.isInteger(minutesStep)) {\n return date;\n }\n\n const minutes = date.getMinutes();\n const roundedMinutes = Math.round(minutes / minutesStep) * minutesStep;\n const diff = roundedMinutes - minutes;\n\n return addMinutes(date, diff);\n } catch {\n return date;\n }\n}\n\n/**\n * Calculate the time difference between two timezones in hours\n *\n * @param fromTimeZone - Source timezone (IANA string)\n * @param toTimeZone - Target timezone (IANA string)\n * @returns Difference in hours (positive if toTimeZone is ahead)\n *\n * @example\n * ```ts\n * getTimeZoneDifference('America/New_York', 'America/Los_Angeles');\n * // -3 (Los Angeles is 3 hours behind New York)\n * ```\n */\nexport function getTimeZoneDifference(fromTimeZone: string, toTimeZone: string): number {\n try {\n if (!fromTimeZone || !toTimeZone || typeof fromTimeZone !== 'string' || typeof toTimeZone !== 'string') {\n return 0;\n }\n\n const now = new Date();\n const fromDate = toZonedTime(now, fromTimeZone);\n const toDate = toZonedTime(now, toTimeZone);\n\n const diff = differenceInHours(toDate, fromDate);\n // Handle NaN case (differenceInHours can return NaN for invalid dates)\n return isNaN(diff) ? 0 : diff;\n } catch {\n return 0;\n }\n}\n\n","/**\n * App ID Resolution Utility\n * @package @jmruthers/pace-core\n * @module Utils/AppIdResolver\n * @since 1.0.0\n * \n * This module provides utilities to resolve app names to app IDs for database operations.\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { createLogger } from '../core/logger';\n\nconst log = createLogger('AppIdResolver');\n\n/**\n * Resolves an app name to its corresponding app ID\n * \n * @param supabase - Supabase client instance\n * @param appName - The app name to resolve\n * @returns Promise resolving to the app ID or null if not found\n */\nexport async function getAppId(\n supabase: SupabaseClient<Database>,\n appName: string\n): Promise<string | null> {\n try {\n const { data, error } = await supabase\n .from('rbac_apps')\n .select('id')\n .ilike('name', appName)\n .eq('is_active', true)\n .single();\n\n if (error) {\n log.error('Failed to resolve app ID for app name:', appName, error);\n return null;\n }\n\n return (data as { id: string } | null)?.id || null;\n } catch (error) {\n log.error('Error resolving app ID for app name:', appName, error);\n return null;\n }\n}\n\n/**\n * Resolves multiple app names to their corresponding app IDs\n * \n * @param supabase - Supabase client instance\n * @param appNames - Array of app names to resolve\n * @returns Promise resolving to a map of app names to app IDs\n */\nexport async function getAppIds(\n supabase: SupabaseClient<Database>,\n appNames: string[]\n): Promise<Record<string, string | null>> {\n try {\n // For case-insensitive matching with multiple values, we need to use OR conditions\n // since PostgreSQL doesn't support case-insensitive IN with ILIKE\n const orConditions = appNames.map(name => `name.ilike.${name}`).join(',');\n \n const { data, error } = await supabase\n .from('rbac_apps')\n .select('id, name')\n .or(orConditions)\n .eq('is_active', true);\n\n if (error) {\n log.error('Failed to resolve app IDs for app names:', appNames, error);\n return {};\n }\n\n const result: Record<string, string | null> = {};\n \n // Initialize all app names with null\n appNames.forEach(name => {\n result[name] = null;\n });\n\n // Set resolved app IDs - match case-insensitively\n (data as { id: string; name: string }[] | null)?.forEach(app => {\n // Find the original app name that matches (case-insensitive)\n const originalName = appNames.find(name => \n name.toLowerCase() === app.name.toLowerCase()\n );\n if (originalName) {\n result[originalName] = app.id;\n }\n });\n\n return result;\n } catch (error) {\n log.error('Error resolving app IDs for app names:', appNames, error);\n return {};\n }\n}\n\n/**\n * Cached app ID resolver with TTL\n */\nexport class CachedAppIdResolver {\n private cache = new Map<string, { id: string | null; expires: number }>();\n private ttl = 5 * 60 * 1000; // 5 minutes\n\n async getAppId(\n supabase: SupabaseClient<Database>,\n appName: string\n ): Promise<string | null> {\n const now = Date.now();\n const cached = this.cache.get(appName);\n\n if (cached && cached.expires > now) {\n return cached.id;\n }\n\n const id = await getAppId(supabase, appName);\n this.cache.set(appName, { id, expires: now + this.ttl });\n\n return id;\n }\n\n clearCache(): void {\n this.cache.clear();\n }\n\n clearCacheForApp(appName: string): void {\n this.cache.delete(appName);\n }\n}\n\n// Export singleton instance\nexport const cachedAppIdResolver = new CachedAppIdResolver();\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/components/LoadingSpinner/LoadingSpinner.tsx","../src/utils/timezone/timezone.ts","../src/utils/app/appIdResolver.ts"],"sourcesContent":["/**\n * @file LoadingSpinner Component\n * @package @jmruthers/pace-core\n * @module Components/LoadingSpinner\n * @since 0.1.0\n *\n * A simple, accessible loading spinner component for indicating loading states.\n * Provides smooth animations with reduced motion support for accessibility.\n *\n * Features:\n * - Multiple size variants (sm, md, lg)\n * - Smooth CSS animations\n * - Reduced motion support for accessibility\n * - Screen reader friendly with proper ARIA attributes\n * - Customizable styling\n * - Lightweight and performant\n *\n * @example\n * ```tsx\n * // Basic loading spinner\n * <LoadingSpinner />\n * \n * // Different sizes\n * <LoadingSpinner size=\"sm\" />\n * <LoadingSpinner size=\"md\" />\n * <LoadingSpinner size=\"lg\" />\n * \n * // With custom styling\n * <LoadingSpinner \n * size=\"lg\" \n * className=\"text-main-500\" \n * />\n * \n * // In a button\n * <Button disabled>\n * <LoadingSpinner size=\"sm\" className=\"mr-2\" />\n * Loading...\n * </Button>\n * ```\n *\n * @accessibility\n * - WCAG 2.1 AA compliant\n * - Proper ARIA role=\"status\"\n * - Screen reader announcement with \"Loading...\" text\n * - Reduced motion support for users with vestibular disorders\n * - High contrast support\n *\n * @performance\n * - CSS-only animations for optimal performance\n * - No JavaScript dependencies\n * - Minimal DOM structure\n * - Efficient rendering\n *\n * @dependencies\n * - React 19+ - Component framework\n * - Tailwind CSS - Styling and animations\n */\n\nimport React from 'react';\n\n/**\n * Props for the LoadingSpinner component\n */\nexport interface LoadingSpinnerProps {\n /** Size variant of the spinner */\n size?: 'sm' | 'md' | 'lg';\n /** Additional CSS classes for styling */\n className?: string;\n}\n\n/**\n * LoadingSpinner component\n * A simple, accessible loading spinner for indicating loading states\n * \n * @param props - Spinner configuration and styling\n * @returns JSX.Element - The rendered loading spinner\n * \n * @example\n * ```tsx\n * <LoadingSpinner size=\"lg\" className=\"text-main-500\" />\n * ```\n */\nexport const LoadingSpinner: React.FC<LoadingSpinnerProps> = ({ \n size = 'md', \n className = '' \n}) => {\n const sizeClasses: Record<'sm' | 'md' | 'lg', string> = {\n sm: 'size-4',\n md: 'size-6',\n lg: 'size-8'\n };\n\n // Ensure we always have a valid size class, defaulting to 'md' if invalid\n const validSize = size && size in sizeClasses ? size : 'md';\n const sizeClass = sizeClasses[validSize];\n\n return (\n <canvas className={`inline-block animate-spin rounded-full border-2 border-solid border-current border-r-transparent motion-reduce:animate-[spin_1.5s_linear_infinite] ${sizeClass} ${className}`.trim()} role=\"status\">\n <span className=\"sr-only\">Loading...</span>\n </canvas>\n );\n};\n","/**\n * @file Timezone Utilities\n * @package @jmruthers/pace-core\n * @module Utils/Timezone\n * @since 0.1.0\n *\n * Comprehensive timezone conversion and formatting utilities using date-fns-tz and native Intl APIs.\n * Provides functions for timezone-aware date operations, conversions, and formatting.\n *\n * Features:\n * - Format dates in specific timezones\n * - Get timezone abbreviations\n * - Convert between UTC and timezone local times\n * - Round dates to nearest minutes\n * - Calculate timezone differences\n * - Get user's browser timezone\n *\n * @example\n * ```ts\n * import { toZonedTime, fromZonedTime, formatInTimeZone, getUserTimeZone } from '@jmruthers/pace-core/utils/timezone';\n *\n * // Convert UTC to local timezone\n * const utcDate = new Date('2024-01-15T10:00:00Z');\n * const localDate = toZonedTime(utcDate, 'America/New_York');\n *\n * // Convert local time to UTC\n * const localInput = new Date(2024, 0, 15, 10, 0);\n * const utcDate = fromZonedTime(localInput, 'America/New_York');\n *\n * // Format with timezone\n * const formatted = formatInTimeZone(utcDate, 'America/New_York', 'MMM dd, yyyy HH:mm');\n * ```\n */\n\nimport { format, parseISO, addMinutes, differenceInHours, isValid } from 'date-fns';\nimport { formatInTimeZone as fnsFormatInTimeZone, toZonedTime as fnsToZonedTime, fromZonedTime as fnsFromZonedTime } from 'date-fns-tz';\n\n/**\n * Format a date in a specific timezone using date-fns format strings\n *\n * @param date - Date to format (Date object, ISO string, or timestamp)\n * @param timeZone - IANA timezone string (e.g., 'America/New_York')\n * @param formatStr - date-fns format string (e.g., 'MMM dd, yyyy HH:mm')\n * @returns Formatted date string or 'Invalid date' on error\n *\n * @example\n * ```ts\n * formatInTimeZone(new Date('2024-01-15T10:00:00Z'), 'America/New_York', 'MMM dd, yyyy HH:mm');\n * // \"Jan 15, 2024 05:00\"\n * ```\n */\nexport function formatInTimeZone(\n date: Date | string | number,\n timeZone: string,\n formatStr: string\n): string {\n try {\n if (!timeZone || typeof timeZone !== 'string') {\n return 'Invalid date';\n }\n\n let dateObj: Date;\n if (typeof date === 'string') {\n dateObj = parseISO(date);\n } else if (typeof date === 'number') {\n dateObj = new Date(date);\n } else {\n dateObj = date;\n }\n\n if (!isValid(dateObj)) {\n return 'Invalid date';\n }\n\n return fnsFormatInTimeZone(dateObj, timeZone, formatStr);\n } catch {\n return 'Invalid date';\n }\n}\n\n/**\n * Get the timezone abbreviation (EST, PST, etc.) for a date in a specific timezone\n *\n * @param date - Date to get abbreviation for\n * @param timeZone - IANA timezone string\n * @returns Timezone abbreviation or timezone name on error\n *\n * @example\n * ```ts\n * getTimezoneAbbreviation(new Date(), 'America/New_York');\n * // \"EST\" or \"EDT\" depending on date\n * ```\n */\nexport function getTimezoneAbbreviation(date: Date, timeZone: string): string {\n try {\n if (!timeZone || typeof timeZone !== 'string') {\n return timeZone || 'UTC';\n }\n\n if (!isValid(date)) {\n return timeZone;\n }\n\n const formatter = new Intl.DateTimeFormat('en-US', {\n timeZone,\n timeZoneName: 'short'\n });\n\n const parts = formatter.formatToParts(date);\n const timeZoneName = parts.find(part => part.type === 'timeZoneName');\n\n return timeZoneName?.value || timeZone;\n } catch {\n return timeZone || 'UTC';\n }\n}\n\n/**\n * Format time only in a specific timezone\n *\n * @param date - Date to format\n * @param timeZone - IANA timezone string\n * @returns Formatted time string (HH:mm format) or 'Invalid date' on error\n *\n * @example\n * ```ts\n * formatTimeInTimeZone(new Date('2024-01-15T10:00:00Z'), 'America/New_York');\n * // \"05:00\"\n * ```\n */\nexport function formatTimeInTimeZone(date: Date | string, timeZone: string): string {\n return formatInTimeZone(date, timeZone, 'HH:mm');\n}\n\n/**\n * Get the user's local timezone from the browser\n *\n * @returns IANA timezone string (e.g., 'America/New_York')\n *\n * @example\n * ```ts\n * const userTz = getUserTimeZone();\n * // \"America/New_York\" or user's actual timezone\n * ```\n */\nexport function getUserTimeZone(): string {\n try {\n if (typeof Intl !== 'undefined' && Intl.DateTimeFormat) {\n return Intl.DateTimeFormat().resolvedOptions().timeZone;\n }\n return 'UTC';\n } catch {\n return 'UTC';\n }\n}\n\n/**\n * Convert a UTC date to a specific timezone's local time representation\n *\n * @param date - UTC date to convert\n * @param timezone - IANA timezone string\n * @returns Date object representing local time in the timezone\n *\n * @example\n * ```ts\n * const utcDate = new Date('2024-01-15T10:00:00Z');\n * const localDate = toZonedTime(utcDate, 'America/New_York');\n * // Returns Date object representing 5:00 AM in New York\n * ```\n */\nexport function toZonedTime(date: Date, timezone: string): Date {\n try {\n if (!timezone || typeof timezone !== 'string') {\n return date;\n }\n\n if (!isValid(date)) {\n return date;\n }\n\n return fnsToZonedTime(date, timezone);\n } catch {\n return date;\n }\n}\n\n/**\n * Convert a local time in a specific timezone to UTC\n *\n * @param localDate - Local date in the timezone\n * @param timezone - IANA timezone string\n * @returns Date object in UTC\n *\n * @example\n * ```ts\n * const localDate = new Date(2024, 0, 15, 10, 0); // Jan 15, 2024 10:00 AM local\n * const utcDate = fromZonedTime(localDate, 'America/New_York');\n * // Returns Date object representing 3:00 PM UTC (if EST) or 2:00 PM UTC (if EDT)\n * ```\n */\nexport function fromZonedTime(localDate: Date, timezone: string): Date {\n try {\n if (!timezone || typeof timezone !== 'string') {\n return localDate;\n }\n\n if (!isValid(localDate)) {\n return localDate;\n }\n\n return fnsFromZonedTime(localDate, timezone);\n } catch {\n return localDate;\n }\n}\n\n/**\n * Round a date to the nearest X minutes\n *\n * @param date - Date to round\n * @param minutesStep - Number of minutes to round to (default: 5)\n * @returns Rounded date\n *\n * @example\n * ```ts\n * const date = new Date('2024-01-15T10:23:00Z');\n * roundToNearestMinutes(date, 5);\n * // Returns Date object for 10:25:00\n * ```\n */\nexport function roundToNearestMinutes(date: Date, minutesStep: number = 5): Date {\n try {\n if (!isValid(date)) {\n return date;\n }\n\n if (minutesStep <= 0 || !Number.isInteger(minutesStep)) {\n return date;\n }\n\n const minutes = date.getMinutes();\n const roundedMinutes = Math.round(minutes / minutesStep) * minutesStep;\n const diff = roundedMinutes - minutes;\n\n return addMinutes(date, diff);\n } catch {\n return date;\n }\n}\n\n/**\n * Calculate the time difference between two timezones in hours\n *\n * @param fromTimeZone - Source timezone (IANA string)\n * @param toTimeZone - Target timezone (IANA string)\n * @returns Difference in hours (positive if toTimeZone is ahead)\n *\n * @example\n * ```ts\n * getTimeZoneDifference('America/New_York', 'America/Los_Angeles');\n * // -3 (Los Angeles is 3 hours behind New York)\n * ```\n */\nexport function getTimeZoneDifference(fromTimeZone: string, toTimeZone: string): number {\n try {\n if (!fromTimeZone || !toTimeZone || typeof fromTimeZone !== 'string' || typeof toTimeZone !== 'string') {\n return 0;\n }\n\n const now = new Date();\n const fromDate = toZonedTime(now, fromTimeZone);\n const toDate = toZonedTime(now, toTimeZone);\n\n const diff = differenceInHours(toDate, fromDate);\n // Handle NaN case (differenceInHours can return NaN for invalid dates)\n return isNaN(diff) ? 0 : diff;\n } catch {\n return 0;\n }\n}\n\n","/**\n * App ID Resolution Utility\n * @package @jmruthers/pace-core\n * @module Utils/AppIdResolver\n * @since 1.0.0\n * \n * This module provides utilities to resolve app names to app IDs for database operations.\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { createLogger } from '../core/logger';\n\nconst log = createLogger('AppIdResolver');\n\n/**\n * Resolves an app name to its corresponding app ID\n * \n * @param supabase - Supabase client instance\n * @param appName - The app name to resolve\n * @returns Promise resolving to the app ID or null if not found\n */\nexport async function getAppId(\n supabase: SupabaseClient<Database>,\n appName: string\n): Promise<string | null> {\n try {\n const { data, error } = await supabase\n .from('rbac_apps')\n .select('id')\n .ilike('name', appName)\n .eq('is_active', true)\n .single();\n\n if (error) {\n log.error('Failed to resolve app ID for app name:', appName, error);\n return null;\n }\n\n return (data as { id: string } | null)?.id || null;\n } catch (error) {\n log.error('Error resolving app ID for app name:', appName, error);\n return null;\n }\n}\n\n/**\n * Resolves multiple app names to their corresponding app IDs\n * \n * @param supabase - Supabase client instance\n * @param appNames - Array of app names to resolve\n * @returns Promise resolving to a map of app names to app IDs\n */\nexport async function getAppIds(\n supabase: SupabaseClient<Database>,\n appNames: string[]\n): Promise<Record<string, string | null>> {\n try {\n // For case-insensitive matching with multiple values, we need to use OR conditions\n // since PostgreSQL doesn't support case-insensitive IN with ILIKE\n const orConditions = appNames.map(name => `name.ilike.${name}`).join(',');\n \n const { data, error } = await supabase\n .from('rbac_apps')\n .select('id, name')\n .or(orConditions)\n .eq('is_active', true);\n\n if (error) {\n log.error('Failed to resolve app IDs for app names:', appNames, error);\n return {};\n }\n\n const result: Record<string, string | null> = {};\n \n // Initialize all app names with null\n appNames.forEach(name => {\n result[name] = null;\n });\n\n // Set resolved app IDs - match case-insensitively\n (data as { id: string; name: string }[] | null)?.forEach(app => {\n // Find the original app name that matches (case-insensitive)\n const originalName = appNames.find(name => \n name.toLowerCase() === app.name.toLowerCase()\n );\n if (originalName) {\n result[originalName] = app.id;\n }\n });\n\n return result;\n } catch (error) {\n log.error('Error resolving app IDs for app names:', appNames, error);\n return {};\n }\n}\n\n/**\n * Cached app ID resolver with TTL\n */\nexport class CachedAppIdResolver {\n private cache = new Map<string, { id: string | null; expires: number }>();\n private ttl = 5 * 60 * 1000; // 5 minutes\n\n async getAppId(\n supabase: SupabaseClient<Database>,\n appName: string\n ): Promise<string | null> {\n const now = Date.now();\n const cached = this.cache.get(appName);\n\n if (cached && cached.expires > now) {\n return cached.id;\n }\n\n const id = await getAppId(supabase, appName);\n this.cache.set(appName, { id, expires: now + this.ttl });\n\n return id;\n }\n\n clearCache(): void {\n this.cache.clear();\n }\n\n clearCacheForApp(appName: string): void {\n this.cache.delete(appName);\n }\n}\n\n// Export singleton instance\nexport const cachedAppIdResolver = new CachedAppIdResolver();\n"],"mappings":"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","names":[]}
@@ -17,10 +17,9 @@ function parseAndNormalizeEventColours(input) {
17
17
  } else if (typeof input !== "object") {
18
18
  return null;
19
19
  }
20
- const pick = (o, standard, legacy) => (o?.[standard] ?? o?.[legacy]) || null;
21
- const main = pick(obj, "main", "ev-main");
22
- const sec = pick(obj, "sec", "ev-sec");
23
- const acc = pick(obj, "acc", "ev-acc");
20
+ const main = obj?.main || null;
21
+ const sec = obj?.sec || null;
22
+ const acc = obj?.acc || null;
24
23
  if (!main && !sec && !acc) return null;
25
24
  const fill = (p) => {
26
25
  if (!p || typeof p !== "object") return {};
@@ -175,4 +174,4 @@ export {
175
174
  isDynamicThemingActive,
176
175
  getCurrentThemeData
177
176
  };
178
- //# sourceMappingURL=chunk-SQGMNID3.js.map
177
+ //# sourceMappingURL=chunk-L4OXEN46.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/theming/parseEventColours.ts","../src/theming/runtime.ts"],"sourcesContent":["/**\n * @file Event Colours Parser\n * @package @jmruthers/pace-core\n * @module Theming/ParseEventColours\n * @since 2.0.0\n * \n * Shared utility for parsing and normalizing event_colours from database.\n * Handles multiple input formats and ensures consistent palette structure.\n */\n\nimport { createLogger } from '../utils/core/logger';\n\nconst log = createLogger('ParseEventColours');\n\n/**\n * Parse and normalize event_colours to PaletteData\n * \n * Supports input formats:\n * - Object with 'main', 'sec', 'acc' keys\n * - JSON string that will be parsed\n * \n * Only includes explicitly defined color values. Does not fill\n * missing shades - only shades that are present in the input will\n * be included in the output.\n * \n * @param input - Event colours from database (JSONB field)\n * @returns Normalized palette data with main, sec, acc palettes, or null if invalid\n * \n * @example\n * ```ts\n * // Standard format\n * const colours = {\n * main: { 500: { L: 0.5, C: 0.2, H: 0 }, raw: { L: 0.5, C: 0.2, H: 0 } },\n * sec: { 500: { L: 0.5, C: 0.2, H: 120 } },\n * acc: { 500: { L: 0.5, C: 0.2, H: 240 } }\n * };\n * const palette = parseAndNormalizeEventColours(colours);\n * // Returns: { main: { 500: {...}, raw: {...} }, sec: { 500: {...} }, acc: { 500: {...} } }\n * ```\n * \n */\nexport function parseAndNormalizeEventColours(input: unknown): { main: any; sec: any; acc: any } | null {\n try {\n if (!input) return null;\n let obj: any = input;\n \n // Handle string input (JSON string from database)\n if (typeof input === 'string') {\n try {\n obj = JSON.parse(input);\n } catch {\n return null;\n }\n } else if (typeof input !== 'object') {\n return null;\n }\n\n // Use standard 'main'/'sec'/'acc' keys\n const main = obj?.main || null;\n const sec = obj?.sec || null;\n const acc = obj?.acc || null;\n \n // If no palette data found, return null\n if (!main && !sec && !acc) return null;\n\n // Helper: only include explicitly defined color values\n // This ensures we don't include undefined shades in the palette\n const fill = (p: any) => {\n if (!p || typeof p !== 'object') return {} as any;\n const out: any = {};\n \n // Only include shades that are explicitly defined in the input\n // Iterate over all keys in the palette object\n for (const key in p) {\n // Only include the key if it has a defined value (not null, undefined, or empty)\n const value = p[key];\n if (value !== null && value !== undefined && value !== '') {\n out[key] = value;\n }\n }\n \n return out;\n };\n\n return { \n main: fill(main), \n sec: fill(sec), \n acc: fill(acc) \n };\n } catch (error) {\n log.warn('Failed to parse/normalize event colours:', error);\n return null;\n }\n}\n\n","/**\n * @file PACE Core Theming Runtime\n * @package @jmruthers/pace-core\n * @module Theming/Runtime\n * @since 2.0.0\n * \n * Runtime helpers for palette replacement from organisation/event payloads.\n * Provides simple applyPalette() and clearPalette() API with SSR support.\n */\n\nimport { createLogger } from '../utils/core/logger';\n\nconst log = createLogger('ThemingRuntime');\n\n/**\n * @example\n * ```ts\n * import { applyPalette, clearPalette } from '@jmruthers/pace-core/theming/runtime';\n * \n * // Apply organisation colors\n * applyPalette({\n * main: { // main palette shades\n * 50: { L: 0.95, C: 0.02, H: 0 },\n * 500: { L: 0.5, C: 0.2, H: 0 }\n * },\n * sec: { // secondary palette shades\n * 50: { L: 0.95, C: 0.02, H: 120 },\n * 500: { L: 0.5, C: 0.2, H: 120 }\n * },\n * acc: { // accent palette shades\n * 50: { L: 0.95, C: 0.02, H: 240 },\n * 500: { L: 0.5, C: 0.2, H: 240 }\n * }\n * });\n * \n * // Clear dynamic theming\n * clearPalette();\n * ```\n */\n\nexport interface ColorShade {\n L: number;\n C: number;\n H: number;\n}\n\nexport interface ColorPalette {\n [shade: string]: ColorShade;\n}\n\nexport interface PaletteData {\n main: ColorPalette;\n sec: ColorPalette;\n acc: ColorPalette;\n}\n\n/**\n * Converts OKLCH color data to CSS string\n */\nfunction formatOklchCss(color: ColorShade): string {\n return `oklch(${color.L} ${color.C} ${color.H})`;\n}\n\n/**\n * Applies a complete palette to the document by setting CSS variables on :root.\n * Uses document.documentElement.style.setProperty() for runtime updates.\n * \n * @param palette - Complete palette data with main, sec, and acc palettes\n */\nexport function applyPalette(palette: PaletteData): void {\n if (typeof document === 'undefined' || document === null) {\n log.warn('Document not available (SSR) - palette not applied');\n return;\n }\n\n const root = document.documentElement;\n const main = palette.main || {};\n const sec = palette.sec || {};\n const acc = palette.acc || {};\n\n // Apply main palette\n if (main.raw) {\n root.style.setProperty('--color-main-raw', formatOklchCss(main.raw));\n }\n for (let i = 50; i <= 950; i += 50) {\n const shade = i.toString();\n if (main[shade]) {\n root.style.setProperty(`--color-main-${shade}`, formatOklchCss(main[shade]));\n }\n }\n\n // Apply sec palette\n if (sec.raw) {\n root.style.setProperty('--color-sec-raw', formatOklchCss(sec.raw));\n }\n for (let i = 50; i <= 950; i += 50) {\n const shade = i.toString();\n if (sec[shade]) {\n root.style.setProperty(`--color-sec-${shade}`, formatOklchCss(sec[shade]));\n }\n }\n\n // Apply acc palette\n if (acc.raw) {\n root.style.setProperty('--color-acc-raw', formatOklchCss(acc.raw));\n }\n for (let i = 50; i <= 950; i += 50) {\n const shade = i.toString();\n if (acc[shade]) {\n root.style.setProperty(`--color-acc-${shade}`, formatOklchCss(acc[shade]));\n }\n }\n\n}\n\n/**\n * Clears dynamic theming by removing CSS variables from :root.\n * This allows the browser to fall back to static CSS values.\n */\nexport function clearPalette(): void {\n if (typeof document === 'undefined' || document === null) {\n return;\n }\n\n const root = document.documentElement;\n const prefixes = ['--color-main-', '--color-sec-', '--color-acc-'];\n \n // Remove all dynamically set color variables\n for (let i = root.style.length - 1; i >= 0; i--) {\n const propertyName = root.style[i];\n if (prefixes.some(prefix => propertyName.startsWith(prefix))) {\n root.style.removeProperty(propertyName);\n }\n }\n}\n\n/**\n * Generates CSS for SSR rendering (for server-side rendering)\n * Creates a :root rule with all color variables\n */\nfunction generateThemeCSS(palette: PaletteData): string {\n // Handle null/undefined palette\n if (!palette) {\n throw new Error('Palette data is required');\n }\n \n const main = palette.main || {};\n const sec = palette.sec || {};\n const acc = palette.acc || {};\n \n let css = ':root {\\n';\n \n // MAIN palette\n css += ' /* MAIN */\\n';\n if (main.raw) {\n css += ` --color-main-raw: ${formatOklchCss(main.raw)};\\n`;\n }\n for (let i = 50; i <= 950; i += 50) {\n const shade = i.toString();\n if (main[shade]) {\n css += ` --color-main-${shade}: ${formatOklchCss(main[shade])};\\n`;\n }\n }\n \n // SEC palette\n css += ' /* SEC */\\n';\n if (sec.raw) {\n css += ` --color-sec-raw: ${formatOklchCss(sec.raw)};\\n`;\n }\n for (let i = 50; i <= 950; i += 50) {\n const shade = i.toString();\n if (sec[shade]) {\n css += ` --color-sec-${shade}: ${formatOklchCss(sec[shade])};\\n`;\n }\n }\n \n // ACC palette\n css += ' /* ACC */\\n';\n if (acc.raw) {\n css += ` --color-acc-raw: ${formatOklchCss(acc.raw)};\\n`;\n }\n for (let i = 50; i <= 950; i += 50) {\n const shade = i.toString();\n if (acc[shade]) {\n css += ` --color-acc-${shade}: ${formatOklchCss(acc[shade])};\\n`;\n }\n }\n \n css += '}\\n';\n \n return css;\n}\n\n/**\n * Generates the same CSS block for SSR rendering\n * Use this in your SSR setup to avoid FOUC\n */\nexport function generateSSRThemeCSS(palette: PaletteData): string {\n return generateThemeCSS(palette);\n}\n\n/**\n * Checks if dynamic theming is currently active\n */\nexport function isDynamicThemingActive(): boolean {\n if (typeof document === 'undefined' || document === null) {\n return false;\n }\n \n const root = document.documentElement;\n // Check if any color variables are set on :root\n return root.style.getPropertyValue('--color-main-500') !== '';\n}\n\n/**\n * Gets the current dynamic theme data (if any)\n * Useful for debugging or persistence\n */\nexport function getCurrentThemeData(): PaletteData | null {\n if (typeof document === 'undefined' || document === null) {\n return null;\n }\n\n const root = document.documentElement;\n const main: ColorPalette = {};\n const sec: ColorPalette = {};\n const acc: ColorPalette = {};\n\n // Extract CSS variables from :root\n // This is a simplified implementation\n // In a real scenario, you might want to parse the actual color values\n // For now, we'll return null as the theme data isn't easily extractable\n return null;\n}\n\n// Re-export parseEventColours utility for convenience\nexport { parseAndNormalizeEventColours } from './parseEventColours';"],"mappings":"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","names":["log"]}
@@ -65,4 +65,4 @@ export {
65
65
  validateHtml,
66
66
  renderSafeHtml
67
67
  };
68
- //# sourceMappingURL=chunk-R77UEZ4E.js.map
68
+ //# sourceMappingURL=chunk-M43Y4SSO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/core/cn.ts","../src/utils/validation/htmlSanitization.ts"],"sourcesContent":["\nimport { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}","/**\n * @file HTML Sanitization Utilities\n * @package @jmruthers/pace-core\n * @module Utils/Validation/HTMLSanitization\n * @since 0.4.36\n * \n * Utilities for safely rendering HTML content.\n * Provides sanitization and validation for basic HTML elements.\n */\n\n/**\n * Allowed HTML tags for safe rendering\n */\nconst ALLOWED_TAGS = [\n 'p', 'div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'strong', 'em', 'b', 'i', 'u', 's', 'mark', 'small', 'sub', 'sup',\n 'ul', 'ol', 'li', 'dl', 'dt', 'dd',\n 'blockquote', 'pre', 'code', 'kbd', 'samp',\n 'a', 'br', 'hr',\n 'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td',\n 'img', 'figure', 'figcaption',\n 'section', 'article', 'aside', 'header', 'footer', 'main', 'nav',\n 'details', 'summary'\n] as const;\n\n/**\n * Allowed HTML attributes for safe rendering\n */\nconst ALLOWED_ATTRIBUTES = [\n 'id', 'class', 'style', 'title', 'lang', 'dir',\n 'href', 'target', 'rel', 'download',\n 'src', 'alt', 'width', 'height', 'loading',\n 'colspan', 'rowspan', 'scope', 'headers',\n 'open', 'datetime', 'cite'\n] as const;\n\n/**\n * Basic HTML sanitization function using regex-based approach\n * Removes potentially dangerous elements and attributes while preserving basic formatting\n * This approach is more reliable in SSR environments and doesn't require DOM manipulation\n * \n * @param html - The HTML string to sanitize\n * @returns Sanitized HTML string safe for rendering\n * \n * @example\n * ```tsx\n * const safeHtml = sanitizeHtml('<p>Hello <strong>world</strong>!</p>');\n * // Returns: '<p>Hello <strong>world</strong>!</p>'\n * \n * const dangerousHtml = sanitizeHtml('<script>alert(\"xss\")</script><p>Safe content</p>');\n * // Returns: '<p>Safe content</p>'\n * ```\n */\nexport function sanitizeHtml(html: string): string {\n if (!html || typeof html !== 'string') {\n return '';\n }\n\n // Basic safety: just remove script tags and dangerous attributes\n let sanitized = html\n // Remove script tags (including self-closing and malformed)\n .replace(/<script\\b[^>]*>.*?<\\/script>/gi, '')\n .replace(/<script\\b[^>]*\\/>/gi, '')\n // Remove iframe tags (including self-closing and malformed)\n .replace(/<iframe\\b[^>]*>.*?<\\/iframe>/gi, '')\n .replace(/<iframe\\b[^>]*\\/>/gi, '')\n // Remove object tags (including self-closing and malformed)\n .replace(/<object\\b[^>]*>.*?<\\/object>/gi, '')\n .replace(/<object\\b[^>]*\\/>/gi, '')\n // Remove embed tags (including self-closing)\n .replace(/<embed\\b[^>]*\\/?>/gi, '')\n // Remove form tags (including self-closing and malformed)\n .replace(/<form\\b[^>]*>.*?<\\/form>/gi, '')\n .replace(/<form\\b[^>]*\\/>/gi, '')\n // Remove input tags (including self-closing)\n .replace(/<input\\b[^>]*\\/?>/gi, '')\n // Remove button tags (including self-closing and malformed)\n .replace(/<button\\b[^>]*>.*?<\\/button>/gi, '')\n .replace(/<button\\b[^>]*\\/>/gi, '')\n // Remove event handlers from any remaining tags - correct pattern\n .replace(/\\s*on\\w+\\s*=\\s*[\"'][^\"']*[\"']/gi, '')\n // Remove javascript: protocols - correct pattern\n .replace(/javascript:[^\"'\\s>]*/gi, '')\n // Remove data: protocols - correct pattern\n .replace(/data:[^\"'\\s>]*/gi, '');\n\n return sanitized;\n}\n\n/**\n * Validates if HTML content is safe for rendering\n * \n * @param html - The HTML string to validate\n * @returns Object with validation result and any warnings\n * \n * @example\n * ```tsx\n * const validation = validateHtml('<p>Safe content</p>');\n * console.log(validation.isValid); // true\n * console.log(validation.warnings); // []\n * ```\n */\nexport function validateHtml(html: string): { isValid: boolean; warnings: string[] } {\n const warnings: string[] = [];\n \n if (!html || typeof html !== 'string') {\n return { isValid: false, warnings: ['HTML content must be a non-empty string'] };\n }\n\n // Check for potentially dangerous patterns\n const dangerousPatterns = [\n { pattern: /<script\\b[^>]*>.*?<\\/script>/gi, message: 'Script tags are not allowed' },\n { pattern: /<script\\b[^>]*\\/>/gi, message: 'Script tags are not allowed' },\n { pattern: /<iframe\\b[^>]*>.*?<\\/iframe>/gi, message: 'Iframe tags are not allowed' },\n { pattern: /<iframe\\b[^>]*\\/>/gi, message: 'Iframe tags are not allowed' },\n { pattern: /<object\\b[^>]*>.*?<\\/object>/gi, message: 'Object tags are not allowed' },\n { pattern: /<object\\b[^>]*\\/>/gi, message: 'Object tags are not allowed' },\n { pattern: /<embed\\b[^>]*\\/?>/gi, message: 'Embed tags are not allowed' },\n { pattern: /<form\\b[^>]*>.*?<\\/form>/gi, message: 'Form tags are not allowed' },\n { pattern: /<form\\b[^>]*\\/>/gi, message: 'Form tags are not allowed' },\n { pattern: /<input\\b[^>]*\\/?>/gi, message: 'Input tags are not allowed' },\n { pattern: /<button\\b[^>]*>.*?<\\/button>/gi, message: 'Button tags are not allowed' },\n { pattern: /<button\\b[^>]*\\/>/gi, message: 'Button tags are not allowed' },\n { pattern: /on\\w+\\s*=/gi, message: 'Event handlers are not allowed' },\n { pattern: /javascript:/gi, message: 'JavaScript protocols are not allowed' },\n { pattern: /data:/gi, message: 'Data protocols are not allowed' }\n ];\n\n dangerousPatterns.forEach(({ pattern, message }) => {\n if (pattern.test(html)) {\n warnings.push(message);\n }\n });\n\n return {\n isValid: warnings.length === 0,\n warnings\n };\n}\n\n/**\n * Safely renders HTML content with sanitization\n * \n * @param html - The HTML string to render\n * @param options - Rendering options\n * @returns Object with sanitized HTML and validation info\n * \n * @example\n * ```tsx\n * const result = renderSafeHtml('<p>Hello <strong>world</strong>!</p>');\n * console.log(result.html); // Sanitized HTML\n * console.log(result.isValid); // true\n * ```\n */\nexport function renderSafeHtml(\n html: string, \n options: { \n strict?: boolean; \n logWarnings?: boolean;\n } = {}\n): { \n html: string; \n isValid: boolean; \n warnings: string[] \n} {\n const { strict = true, logWarnings = false } = options;\n \n const validation = validateHtml(html);\n const sanitizedHtml = sanitizeHtml(html);\n \n if (logWarnings && validation.warnings.length > 0) {\n // Use logger if needed, but this is controlled by logWarnings option\n // For now, keep console.warn as it's only called when explicitly requested\n // via logWarnings option, which is typically for debugging\n console.warn('HTML content warnings:', validation.warnings);\n }\n \n return {\n html: sanitizedHtml,\n isValid: validation.isValid,\n warnings: validation.warnings\n };\n}\n\n"],"mappings":";AACA,SAA0B,YAAY;AACtC,SAAS,eAAe;AAEjB,SAAS,MAAM,QAA8B;AAClD,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;;;AC+CO,SAAS,aAAa,MAAsB;AACjD,MAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,WAAO;AAAA,EACT;AAGA,MAAI,YAAY,KAEb,QAAQ,kCAAkC,EAAE,EAC5C,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,kCAAkC,EAAE,EAC5C,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,kCAAkC,EAAE,EAC5C,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,8BAA8B,EAAE,EACxC,QAAQ,qBAAqB,EAAE,EAE/B,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,kCAAkC,EAAE,EAC5C,QAAQ,uBAAuB,EAAE,EAEjC,QAAQ,mCAAmC,EAAE,EAE7C,QAAQ,0BAA0B,EAAE,EAEpC,QAAQ,oBAAoB,EAAE;AAEjC,SAAO;AACT;AAeO,SAAS,aAAa,MAAwD;AACnF,QAAM,WAAqB,CAAC;AAE5B,MAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,WAAO,EAAE,SAAS,OAAO,UAAU,CAAC,yCAAyC,EAAE;AAAA,EACjF;AAGA,QAAM,oBAAoB;AAAA,IACxB,EAAE,SAAS,kCAAkC,SAAS,8BAA8B;AAAA,IACpF,EAAE,SAAS,uBAAuB,SAAS,8BAA8B;AAAA,IACzE,EAAE,SAAS,kCAAkC,SAAS,8BAA8B;AAAA,IACpF,EAAE,SAAS,uBAAuB,SAAS,8BAA8B;AAAA,IACzE,EAAE,SAAS,kCAAkC,SAAS,8BAA8B;AAAA,IACpF,EAAE,SAAS,uBAAuB,SAAS,8BAA8B;AAAA,IACzE,EAAE,SAAS,uBAAuB,SAAS,6BAA6B;AAAA,IACxE,EAAE,SAAS,8BAA8B,SAAS,4BAA4B;AAAA,IAC9E,EAAE,SAAS,qBAAqB,SAAS,4BAA4B;AAAA,IACrE,EAAE,SAAS,uBAAuB,SAAS,6BAA6B;AAAA,IACxE,EAAE,SAAS,kCAAkC,SAAS,8BAA8B;AAAA,IACpF,EAAE,SAAS,uBAAuB,SAAS,8BAA8B;AAAA,IACzE,EAAE,SAAS,eAAe,SAAS,iCAAiC;AAAA,IACpE,EAAE,SAAS,iBAAiB,SAAS,uCAAuC;AAAA,IAC5E,EAAE,SAAS,WAAW,SAAS,iCAAiC;AAAA,EAClE;AAEA,oBAAkB,QAAQ,CAAC,EAAE,SAAS,QAAQ,MAAM;AAClD,QAAI,QAAQ,KAAK,IAAI,GAAG;AACtB,eAAS,KAAK,OAAO;AAAA,IACvB;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,SAAS,SAAS,WAAW;AAAA,IAC7B;AAAA,EACF;AACF;AAgBO,SAAS,eACd,MACA,UAGI,CAAC,GAKL;AACA,QAAM,EAAE,SAAS,MAAM,cAAc,MAAM,IAAI;AAE/C,QAAM,aAAa,aAAa,IAAI;AACpC,QAAM,gBAAgB,aAAa,IAAI;AAEvC,MAAI,eAAe,WAAW,SAAS,SAAS,GAAG;AAIjD,YAAQ,KAAK,0BAA0B,WAAW,QAAQ;AAAA,EAC5D;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,WAAW;AAAA,IACpB,UAAU,WAAW;AAAA,EACvB;AACF;","names":[]}