@jmruthers/pace-core 0.5.189 → 0.5.191

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 (438) hide show
  1. package/core-usage-manifest.json +0 -4
  2. package/dist/{AuthService-B-cd2MA4.d.ts → AuthService-CbP_utw2.d.ts} +7 -3
  3. package/dist/{DataTable-IVYljGJ6.d.ts → DataTable-Be6dH_dR.d.ts} +1 -1
  4. package/dist/{DataTable-GUFUNZ3N.js → DataTable-WKRZD47S.js} +8 -8
  5. package/dist/{PublicPageProvider-B8HaLe69.d.ts → PublicPageProvider-ULXC_u6U.d.ts} +84 -25
  6. package/dist/{UnifiedAuthProvider-BG0AL5eE.d.ts → UnifiedAuthProvider-BYA9qB-o.d.ts} +4 -3
  7. package/dist/{UnifiedAuthProvider-643PUAIM.js → UnifiedAuthProvider-FTSG5XH7.js} +4 -2
  8. package/dist/{api-YP7XD5L6.js → api-IHKALJZD.js} +4 -2
  9. package/dist/{chunk-VGZZXKBR.js → chunk-6LTQQAT6.js} +351 -157
  10. package/dist/chunk-6LTQQAT6.js.map +1 -0
  11. package/dist/{chunk-MX64ZF6I.js → chunk-6TQDD426.js} +15 -15
  12. package/dist/chunk-6TQDD426.js.map +1 -0
  13. package/dist/{chunk-YHCN776L.js → chunk-G37KK66H.js} +2 -75
  14. package/dist/chunk-G37KK66H.js.map +1 -0
  15. package/dist/{chunk-THRPYOFK.js → chunk-HW3OVDUF.js} +5 -5
  16. package/dist/chunk-HW3OVDUF.js.map +1 -0
  17. package/dist/{chunk-F2IMUDXZ.js → chunk-I7PSE6JW.js} +75 -2
  18. package/dist/chunk-I7PSE6JW.js.map +1 -0
  19. package/dist/{chunk-IM4QE42D.js → chunk-LOMZXPSN.js} +141 -326
  20. package/dist/chunk-LOMZXPSN.js.map +1 -0
  21. package/dist/chunk-OETXORNB.js +614 -0
  22. package/dist/chunk-OETXORNB.js.map +1 -0
  23. package/dist/{chunk-HESYZWZW.js → chunk-QWWZ5CAQ.js} +2 -2
  24. package/dist/{chunk-HEHYGYOX.js → chunk-ROXMHMY2.js} +403 -46
  25. package/dist/chunk-ROXMHMY2.js.map +1 -0
  26. package/dist/{chunk-2UUZZJFT.js → chunk-ULHIJK66.js} +228 -177
  27. package/dist/{chunk-2UUZZJFT.js.map → chunk-ULHIJK66.js.map} +1 -1
  28. package/dist/{chunk-YGPFYGA6.js → chunk-VKB2CO4Z.js} +838 -503
  29. package/dist/chunk-VKB2CO4Z.js.map +1 -0
  30. package/dist/{chunk-3GOZZZYH.js → chunk-VRGWKHDB.js} +238 -301
  31. package/dist/chunk-VRGWKHDB.js.map +1 -0
  32. package/dist/{chunk-UCQSRW7Z.js → chunk-XNYQOL3Z.js} +431 -384
  33. package/dist/chunk-XNYQOL3Z.js.map +1 -0
  34. package/dist/{chunk-DDM4CCYT.js → chunk-XYXSXPUK.js} +79 -59
  35. package/dist/chunk-XYXSXPUK.js.map +1 -0
  36. package/dist/{chunk-SAUPYVLF.js → chunk-ZSAAAMVR.js} +1 -1
  37. package/dist/chunk-ZSAAAMVR.js.map +1 -0
  38. package/dist/components.d.ts +5 -6
  39. package/dist/components.js +19 -19
  40. package/dist/components.js.map +1 -1
  41. package/dist/{database.generated-DI89OQeI.d.ts → database.generated-CzIvgcPu.d.ts} +165 -201
  42. package/dist/eslint-rules/pace-core-compliance.cjs +0 -2
  43. package/dist/{file-reference-D037xOFK.d.ts → file-reference-BavO2eQj.d.ts} +13 -10
  44. package/dist/hooks.d.ts +20 -15
  45. package/dist/hooks.js +14 -8
  46. package/dist/hooks.js.map +1 -1
  47. package/dist/index.d.ts +17 -15
  48. package/dist/index.js +86 -81
  49. package/dist/index.js.map +1 -1
  50. package/dist/providers.d.ts +3 -3
  51. package/dist/providers.js +3 -1
  52. package/dist/rbac/index.d.ts +77 -13
  53. package/dist/rbac/index.js +12 -9
  54. package/dist/{types-Bwgl--Xo.d.ts → types-CEpcvwwF.d.ts} +1 -1
  55. package/dist/types.d.ts +3 -3
  56. package/dist/types.js +1 -1
  57. package/dist/{usePublicRouteParams-CTDELQ7H.d.ts → usePublicRouteParams-TZe0gy-4.d.ts} +17 -10
  58. package/dist/utils.d.ts +8 -8
  59. package/dist/utils.js +16 -16
  60. package/docs/README.md +2 -2
  61. package/docs/api/classes/ColumnFactory.md +1 -1
  62. package/docs/api/classes/ErrorBoundary.md +1 -1
  63. package/docs/api/classes/InvalidScopeError.md +2 -2
  64. package/docs/api/classes/Logger.md +1 -1
  65. package/docs/api/classes/MissingUserContextError.md +2 -2
  66. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  67. package/docs/api/classes/PermissionDeniedError.md +1 -1
  68. package/docs/api/classes/RBACAuditManager.md +2 -2
  69. package/docs/api/classes/RBACCache.md +1 -1
  70. package/docs/api/classes/RBACEngine.md +5 -5
  71. package/docs/api/classes/RBACError.md +1 -1
  72. package/docs/api/classes/RBACNotInitializedError.md +2 -2
  73. package/docs/api/classes/SecureSupabaseClient.md +25 -20
  74. package/docs/api/classes/StorageUtils.md +7 -4
  75. package/docs/api/enums/FileCategory.md +1 -1
  76. package/docs/api/enums/LogLevel.md +1 -1
  77. package/docs/api/enums/RBACErrorCode.md +1 -1
  78. package/docs/api/enums/RPCFunction.md +1 -1
  79. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  80. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  81. package/docs/api/interfaces/AggregateConfig.md +1 -1
  82. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  83. package/docs/api/interfaces/AvatarProps.md +1 -1
  84. package/docs/api/interfaces/BadgeProps.md +1 -1
  85. package/docs/api/interfaces/ButtonProps.md +1 -1
  86. package/docs/api/interfaces/CalendarProps.md +20 -6
  87. package/docs/api/interfaces/CardProps.md +1 -1
  88. package/docs/api/interfaces/ColorPalette.md +1 -1
  89. package/docs/api/interfaces/ColorShade.md +1 -1
  90. package/docs/api/interfaces/ComplianceResult.md +1 -1
  91. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  92. package/docs/api/interfaces/DataRecord.md +1 -1
  93. package/docs/api/interfaces/DataTableAction.md +1 -1
  94. package/docs/api/interfaces/DataTableColumn.md +1 -1
  95. package/docs/api/interfaces/DataTableProps.md +1 -1
  96. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  97. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  98. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  99. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  100. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  101. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  102. package/docs/api/interfaces/ExportColumn.md +1 -1
  103. package/docs/api/interfaces/ExportOptions.md +1 -1
  104. package/docs/api/interfaces/FileDisplayProps.md +62 -16
  105. package/docs/api/interfaces/FileMetadata.md +1 -1
  106. package/docs/api/interfaces/FileReference.md +2 -2
  107. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  108. package/docs/api/interfaces/FileUploadOptions.md +26 -12
  109. package/docs/api/interfaces/FileUploadProps.md +30 -19
  110. package/docs/api/interfaces/FooterProps.md +1 -1
  111. package/docs/api/interfaces/FormFieldProps.md +1 -1
  112. package/docs/api/interfaces/FormProps.md +1 -1
  113. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  114. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  115. package/docs/api/interfaces/InputProps.md +1 -1
  116. package/docs/api/interfaces/LabelProps.md +1 -1
  117. package/docs/api/interfaces/LoggerConfig.md +1 -1
  118. package/docs/api/interfaces/LoginFormProps.md +1 -1
  119. package/docs/api/interfaces/NavigationAccessRecord.md +10 -10
  120. package/docs/api/interfaces/NavigationContextType.md +9 -9
  121. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  122. package/docs/api/interfaces/NavigationItem.md +1 -1
  123. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  124. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  125. package/docs/api/interfaces/Organisation.md +1 -1
  126. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  127. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  128. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  129. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  130. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  131. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  132. package/docs/api/interfaces/PageAccessRecord.md +8 -8
  133. package/docs/api/interfaces/PagePermissionContextType.md +8 -8
  134. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  135. package/docs/api/interfaces/PagePermissionProviderProps.md +7 -7
  136. package/docs/api/interfaces/PaletteData.md +1 -1
  137. package/docs/api/interfaces/ParsedAddress.md +2 -2
  138. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  139. package/docs/api/interfaces/ProgressProps.md +3 -11
  140. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  141. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  142. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  143. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  144. package/docs/api/interfaces/QuickFix.md +1 -1
  145. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  146. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  147. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  148. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  149. package/docs/api/interfaces/RBACConfig.md +2 -2
  150. package/docs/api/interfaces/RBACContext.md +1 -1
  151. package/docs/api/interfaces/RBACLogger.md +1 -1
  152. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  153. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  154. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  155. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  156. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  157. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  158. package/docs/api/interfaces/RBACResult.md +1 -1
  159. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  160. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  161. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  162. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  163. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  164. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  165. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  166. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  167. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  168. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  169. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  170. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  171. package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
  172. package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
  173. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  174. package/docs/api/interfaces/RouteAccessRecord.md +10 -10
  175. package/docs/api/interfaces/RouteConfig.md +10 -10
  176. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  177. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  178. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  179. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  180. package/docs/api/interfaces/SetupIssue.md +1 -1
  181. package/docs/api/interfaces/StorageConfig.md +4 -4
  182. package/docs/api/interfaces/StorageFileInfo.md +7 -7
  183. package/docs/api/interfaces/StorageFileMetadata.md +25 -14
  184. package/docs/api/interfaces/StorageListOptions.md +22 -9
  185. package/docs/api/interfaces/StorageListResult.md +4 -4
  186. package/docs/api/interfaces/StorageUploadOptions.md +21 -8
  187. package/docs/api/interfaces/StorageUploadResult.md +6 -6
  188. package/docs/api/interfaces/StorageUrlOptions.md +19 -6
  189. package/docs/api/interfaces/StyleImport.md +1 -1
  190. package/docs/api/interfaces/SwitchProps.md +1 -1
  191. package/docs/api/interfaces/TabsContentProps.md +1 -1
  192. package/docs/api/interfaces/TabsListProps.md +1 -1
  193. package/docs/api/interfaces/TabsProps.md +1 -1
  194. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  195. package/docs/api/interfaces/TextareaProps.md +1 -1
  196. package/docs/api/interfaces/ToastActionElement.md +1 -1
  197. package/docs/api/interfaces/ToastProps.md +1 -1
  198. package/docs/api/interfaces/UnifiedAuthContextType.md +53 -53
  199. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  200. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  201. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  202. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  203. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  204. package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
  205. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  206. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  207. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  208. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
  209. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  210. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  211. package/docs/api/interfaces/UseResolvedScopeOptions.md +5 -5
  212. package/docs/api/interfaces/UseResolvedScopeReturn.md +4 -4
  213. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  214. package/docs/api/interfaces/UserEventAccess.md +11 -11
  215. package/docs/api/interfaces/UserMenuProps.md +1 -1
  216. package/docs/api/interfaces/UserProfile.md +1 -1
  217. package/docs/api/modules.md +165 -106
  218. package/docs/api-reference/components.md +15 -7
  219. package/docs/api-reference/providers.md +2 -2
  220. package/docs/api-reference/rpc-functions.md +1 -0
  221. package/docs/best-practices/README.md +1 -1
  222. package/docs/best-practices/deployment.md +8 -8
  223. package/docs/getting-started/examples/README.md +2 -2
  224. package/docs/getting-started/installation-guide.md +4 -4
  225. package/docs/getting-started/quick-start.md +3 -3
  226. package/docs/migration/MIGRATION_GUIDE.md +3 -3
  227. package/docs/migration/README.md +18 -0
  228. package/docs/migration/database-changes-december-2025.md +767 -0
  229. package/docs/migration/person-scoped-profiles-migration-guide.md +472 -0
  230. package/docs/rbac/compliance/compliance-guide.md +2 -2
  231. package/docs/rbac/event-based-apps.md +2 -2
  232. package/docs/rbac/getting-started.md +2 -2
  233. package/docs/rbac/quick-start.md +2 -2
  234. package/docs/security/README.md +4 -4
  235. package/docs/standards/07-rbac-and-rls-standard.md +430 -7
  236. package/docs/troubleshooting/README.md +2 -2
  237. package/docs/troubleshooting/migration.md +3 -3
  238. package/package.json +1 -3
  239. package/scripts/check-pace-core-compliance.cjs +1 -1
  240. package/scripts/check-pace-core-compliance.js +1 -1
  241. package/src/__tests__/fixtures/supabase.ts +301 -0
  242. package/src/__tests__/public-recipe-view.test.ts +19 -19
  243. package/src/__tests__/rls-policies.test.ts +210 -74
  244. package/src/components/AddressField/AddressField.test.tsx +42 -0
  245. package/src/components/AddressField/AddressField.tsx +71 -60
  246. package/src/components/AddressField/README.md +7 -6
  247. package/src/components/Alert/Alert.test.tsx +50 -10
  248. package/src/components/Alert/Alert.tsx +5 -3
  249. package/src/components/Avatar/Avatar.test.tsx +95 -43
  250. package/src/components/Avatar/Avatar.tsx +16 -16
  251. package/src/components/Button/Button.test.tsx +2 -1
  252. package/src/components/Button/Button.tsx +3 -3
  253. package/src/components/Calendar/Calendar.test.tsx +53 -37
  254. package/src/components/Calendar/Calendar.tsx +409 -82
  255. package/src/components/Card/Card.test.tsx +7 -4
  256. package/src/components/Card/Card.tsx +3 -6
  257. package/src/components/Checkbox/Checkbox.tsx +2 -2
  258. package/src/components/DataTable/components/ActionButtons.tsx +5 -5
  259. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
  260. package/src/components/DataTable/components/ColumnFilter.tsx +1 -1
  261. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +3 -3
  262. package/src/components/DataTable/components/DataTableBody.tsx +12 -12
  263. package/src/components/DataTable/components/DataTableCore.tsx +3 -3
  264. package/src/components/DataTable/components/DataTableToolbar.tsx +5 -5
  265. package/src/components/DataTable/components/DraggableColumnHeader.tsx +3 -3
  266. package/src/components/DataTable/components/EditableRow.tsx +2 -2
  267. package/src/components/DataTable/components/EmptyState.tsx +3 -3
  268. package/src/components/DataTable/components/GroupHeader.tsx +2 -2
  269. package/src/components/DataTable/components/GroupingDropdown.tsx +1 -1
  270. package/src/components/DataTable/components/ImportModal.tsx +4 -4
  271. package/src/components/DataTable/components/LoadingState.tsx +1 -1
  272. package/src/components/DataTable/components/PaginationControls.tsx +11 -11
  273. package/src/components/DataTable/components/UnifiedTableBody.tsx +9 -9
  274. package/src/components/DataTable/components/ViewRowModal.tsx +2 -2
  275. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +11 -37
  276. package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +157 -0
  277. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +2 -1
  278. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +128 -0
  279. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +19 -0
  280. package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +51 -0
  281. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +84 -0
  282. package/src/components/DataTable/core/__tests__/DataManager.test.ts +14 -0
  283. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +136 -0
  284. package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +16 -0
  285. package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +18 -0
  286. package/src/components/DataTable/hooks/useDataTablePermissions.ts +28 -7
  287. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +30 -1
  288. package/src/components/DataTable/utils/hierarchicalUtils.ts +38 -10
  289. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -3
  290. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +4 -4
  291. package/src/components/Dialog/Dialog.tsx +2 -2
  292. package/src/components/EventSelector/EventSelector.tsx +7 -7
  293. package/src/components/FileDisplay/FileDisplay.tsx +291 -179
  294. package/src/components/FileUpload/FileUpload.tsx +7 -4
  295. package/src/components/Header/Header.test.tsx +28 -0
  296. package/src/components/Header/Header.tsx +22 -9
  297. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +2 -2
  298. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +19 -14
  299. package/src/components/LoadingSpinner/LoadingSpinner.tsx +5 -5
  300. package/src/components/NavigationMenu/NavigationMenu.test.tsx +127 -1
  301. package/src/components/OrganisationSelector/OrganisationSelector.tsx +42 -22
  302. package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +4 -0
  303. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +3 -0
  304. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +3 -0
  305. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +16 -6
  306. package/src/components/PaceAppLayout/PaceAppLayout.tsx +37 -3
  307. package/src/components/PaceAppLayout/test-setup.tsx +1 -0
  308. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +66 -45
  309. package/src/components/PaceLoginPage/PaceLoginPage.tsx +6 -4
  310. package/src/components/Progress/Progress.test.tsx +18 -19
  311. package/src/components/Progress/Progress.tsx +31 -32
  312. package/src/components/PublicLayout/PublicLayout.test.tsx +6 -6
  313. package/src/components/PublicLayout/PublicPageProvider.tsx +5 -3
  314. package/src/components/Select/Select.test.tsx +4 -1
  315. package/src/components/Select/Select.tsx +65 -20
  316. package/src/components/Switch/Switch.test.tsx +2 -1
  317. package/src/components/Switch/Switch.tsx +1 -1
  318. package/src/components/Toast/Toast.tsx +1 -1
  319. package/src/components/Tooltip/Tooltip.test.tsx +8 -2
  320. package/src/components/UserMenu/UserMenu.tsx +3 -3
  321. package/src/eslint-rules/pace-core-compliance.cjs +0 -2
  322. package/src/eslint-rules/pace-core-compliance.js +0 -2
  323. package/src/hooks/__tests__/hooks.integration.test.tsx +4 -1
  324. package/src/hooks/__tests__/useAppConfig.unit.test.ts +76 -5
  325. package/src/hooks/__tests__/useDataTableState.test.ts +76 -0
  326. package/src/hooks/__tests__/useFileUrl.unit.test.ts +25 -69
  327. package/src/hooks/__tests__/useFileUrlCache.test.ts +129 -0
  328. package/src/hooks/__tests__/usePreventTabReload.test.ts +88 -0
  329. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +1 -1
  330. package/src/hooks/__tests__/usePublicEvent.test.ts +608 -0
  331. package/src/hooks/__tests__/useQueryCache.test.ts +144 -0
  332. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +67 -24
  333. package/src/hooks/index.ts +1 -1
  334. package/src/hooks/public/usePublicEvent.ts +10 -10
  335. package/src/hooks/public/usePublicFileDisplay.ts +173 -87
  336. package/src/hooks/useAppConfig.ts +24 -5
  337. package/src/hooks/useFileDisplay.ts +298 -36
  338. package/src/hooks/useFileReference.ts +56 -11
  339. package/src/hooks/useFileUrl.ts +1 -1
  340. package/src/hooks/useInactivityTracker.ts +16 -7
  341. package/src/hooks/usePermissionCache.test.ts +85 -8
  342. package/src/hooks/useQueryCache.ts +27 -6
  343. package/src/hooks/useSecureDataAccess.test.ts +87 -42
  344. package/src/hooks/useSecureDataAccess.ts +95 -48
  345. package/src/providers/__tests__/OrganisationProvider.test.tsx +27 -21
  346. package/src/providers/services/EventServiceProvider.tsx +37 -17
  347. package/src/providers/services/InactivityServiceProvider.tsx +4 -4
  348. package/src/providers/services/OrganisationServiceProvider.tsx +8 -1
  349. package/src/providers/services/UnifiedAuthProvider.tsx +115 -29
  350. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +451 -0
  351. package/src/rbac/__tests__/engine.comprehensive.test.ts +12 -0
  352. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +8 -0
  353. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +4 -0
  354. package/src/rbac/api.ts +240 -36
  355. package/src/rbac/cache-invalidation.ts +21 -7
  356. package/src/rbac/compliance/quick-fix-suggestions.ts +1 -1
  357. package/src/rbac/components/NavigationGuard.tsx +23 -63
  358. package/src/rbac/components/NavigationProvider.test.tsx +52 -23
  359. package/src/rbac/components/NavigationProvider.tsx +13 -11
  360. package/src/rbac/components/PagePermissionGuard.tsx +77 -203
  361. package/src/rbac/components/PagePermissionProvider.tsx +13 -11
  362. package/src/rbac/components/PermissionEnforcer.tsx +24 -62
  363. package/src/rbac/components/RoleBasedRouter.tsx +14 -12
  364. package/src/rbac/components/SecureDataProvider.tsx +13 -11
  365. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +104 -41
  366. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +49 -12
  367. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +22 -1
  368. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +161 -82
  369. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +22 -1
  370. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +77 -30
  371. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +39 -5
  372. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +47 -4
  373. package/src/rbac/engine.ts +4 -2
  374. package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +144 -52
  375. package/src/rbac/hooks/index.ts +3 -0
  376. package/src/rbac/hooks/useCan.test.ts +101 -53
  377. package/src/rbac/hooks/usePermissions.ts +108 -41
  378. package/src/rbac/hooks/useRBAC.test.ts +11 -3
  379. package/src/rbac/hooks/useRBAC.ts +83 -40
  380. package/src/rbac/hooks/useResolvedScope.test.ts +189 -63
  381. package/src/rbac/hooks/useResolvedScope.ts +128 -70
  382. package/src/rbac/hooks/useSecureSupabase.ts +36 -19
  383. package/src/rbac/hooks/useSuperAdminBypass.ts +126 -0
  384. package/src/rbac/request-deduplication.ts +1 -1
  385. package/src/rbac/secureClient.ts +72 -12
  386. package/src/rbac/security.ts +29 -23
  387. package/src/rbac/types.ts +10 -0
  388. package/src/rbac/utils/__tests__/contextValidator.test.ts +150 -0
  389. package/src/rbac/utils/__tests__/deep-equal.test.ts +53 -0
  390. package/src/rbac/utils/__tests__/eventContext.test.ts +8 -3
  391. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +74 -12
  392. package/src/rbac/utils/contextValidator.ts +288 -0
  393. package/src/rbac/utils/eventContext.ts +52 -3
  394. package/src/services/AuthService.ts +37 -8
  395. package/src/services/EventService.ts +165 -21
  396. package/src/services/OrganisationService.ts +125 -137
  397. package/src/services/__tests__/EventService.test.ts +26 -21
  398. package/src/services/__tests__/OrganisationService.pagination.test.ts +34 -8
  399. package/src/services/__tests__/OrganisationService.test.ts +218 -86
  400. package/src/types/database.generated.ts +166 -201
  401. package/src/types/file-reference.ts +13 -10
  402. package/src/types/supabase.ts +2 -2
  403. package/src/utils/__tests__/secureDataAccess.unit.test.ts +3 -2
  404. package/src/utils/app/appNameResolver.test.ts +346 -73
  405. package/src/utils/context/superAdminOverride.ts +58 -0
  406. package/src/utils/file-reference/index.ts +65 -37
  407. package/src/utils/google-places/googlePlacesUtils.test.ts +98 -0
  408. package/src/utils/google-places/googlePlacesUtils.ts +1 -1
  409. package/src/utils/google-places/loadGoogleMapsScript.test.ts +83 -0
  410. package/src/utils/google-places/types.ts +1 -1
  411. package/src/utils/request-deduplication.ts +4 -4
  412. package/src/utils/security/secureDataAccess.test.ts +1 -1
  413. package/src/utils/security/secureDataAccess.ts +7 -4
  414. package/src/utils/storage/README.md +1 -1
  415. package/src/utils/storage/helpers.test.ts +1 -1
  416. package/src/utils/storage/helpers.ts +38 -19
  417. package/src/utils/storage/types.ts +15 -8
  418. package/src/utils/validation/__tests__/csrf.test.ts +105 -0
  419. package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +92 -0
  420. package/src/vite-env.d.ts +2 -2
  421. package/dist/chunk-3GOZZZYH.js.map +0 -1
  422. package/dist/chunk-DDM4CCYT.js.map +0 -1
  423. package/dist/chunk-E7UAOUMY.js +0 -75
  424. package/dist/chunk-E7UAOUMY.js.map +0 -1
  425. package/dist/chunk-F2IMUDXZ.js.map +0 -1
  426. package/dist/chunk-HEHYGYOX.js.map +0 -1
  427. package/dist/chunk-IM4QE42D.js.map +0 -1
  428. package/dist/chunk-MX64ZF6I.js.map +0 -1
  429. package/dist/chunk-SAUPYVLF.js.map +0 -1
  430. package/dist/chunk-THRPYOFK.js.map +0 -1
  431. package/dist/chunk-UCQSRW7Z.js.map +0 -1
  432. package/dist/chunk-VGZZXKBR.js.map +0 -1
  433. package/dist/chunk-YGPFYGA6.js.map +0 -1
  434. package/dist/chunk-YHCN776L.js.map +0 -1
  435. /package/dist/{DataTable-GUFUNZ3N.js.map → DataTable-WKRZD47S.js.map} +0 -0
  436. /package/dist/{UnifiedAuthProvider-643PUAIM.js.map → UnifiedAuthProvider-FTSG5XH7.js.map} +0 -0
  437. /package/dist/{api-YP7XD5L6.js.map → api-IHKALJZD.js.map} +0 -0
  438. /package/dist/{chunk-HESYZWZW.js.map → chunk-QWWZ5CAQ.js.map} +0 -0
@@ -12,8 +12,15 @@
12
12
  * - Debounced input with caching
13
13
  * - Keyboard navigation (Arrow keys, Enter, Escape)
14
14
  * - Accessible ARIA attributes
15
+ * - Semantic HTML (description list for suggestions)
15
16
  * - Loading and error states
16
17
  * - place_id storage for later retrieval
18
+ *
19
+ * @accessibility
20
+ * - Uses semantic HTML: `<dl>`, `<dt>`, `<dd>` for address suggestions
21
+ * - Proper ARIA attributes for combobox pattern
22
+ * - Keyboard navigation support
23
+ * - Screen reader friendly
17
24
  */
18
25
 
19
26
  import * as React from 'react';
@@ -74,8 +81,8 @@ const AddressField = React.forwardRef<HTMLInputElement, AddressFieldProps>(
74
81
  const [inputFocused, setInputFocused] = React.useState(false);
75
82
 
76
83
  const inputRef = React.useRef<HTMLInputElement>(null);
77
- const suggestionsRef = React.useRef<HTMLUListElement>(null);
78
- const containerRef = React.useRef<HTMLDivElement>(null);
84
+ const suggestionsRef = React.useRef<HTMLDListElement>(null);
85
+ const containerRef = React.useRef<HTMLFormElement>(null);
79
86
 
80
87
  // Use controlled or uncontrolled value
81
88
  const value = controlledValue !== undefined ? controlledValue : internalValue;
@@ -216,57 +223,61 @@ const AddressField = React.forwardRef<HTMLInputElement, AddressFieldProps>(
216
223
  }
217
224
  }, [isOpen]);
218
225
 
226
+ // Generate unique ID for suggestions list
227
+ const suggestionsId = React.useId();
228
+
219
229
  // Scroll selected item into view
230
+ // Uses getElementById instead of querySelector to handle React.useId() generated IDs
231
+ // which may contain colons (e.g., ':r15:') that are invalid in CSS selectors
220
232
  React.useEffect(() => {
221
- if (selectedIndex >= 0 && suggestionsRef.current) {
222
- const selectedItem = suggestionsRef.current.children[selectedIndex] as HTMLElement;
233
+ if (selectedIndex >= 0) {
234
+ const selectedItem = document.getElementById(
235
+ `${suggestionsId}-item-${selectedIndex}`
236
+ ) as HTMLElement;
223
237
  if (selectedItem && typeof selectedItem.scrollIntoView === 'function') {
224
238
  selectedItem.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
225
239
  }
226
240
  }
227
- }, [selectedIndex]);
241
+ }, [selectedIndex, suggestionsId]);
228
242
 
229
243
  // Combine refs
230
244
  React.useImperativeHandle(ref, () => inputRef.current as HTMLInputElement);
231
245
 
232
- const suggestionsId = React.useId();
233
246
  const hasError = error || !!autocompleteError;
234
247
 
235
248
  return (
236
- <div ref={containerRef} className={cn('relative w-full', className)}>
237
- <div className="relative">
238
- <Input
239
- ref={inputRef}
240
- type="text"
241
- value={value}
242
- onChange={handleInputChange}
243
- onKeyDown={handleKeyDown}
244
- onFocus={handleFocus}
245
- onBlur={handleBlur}
246
- placeholder={placeholder}
247
- disabled={disabled}
248
- error={hasError}
249
- size={size}
250
- variant={variant}
251
- role="combobox"
252
- aria-expanded={isOpen}
253
- aria-autocomplete="list"
254
- aria-controls={suggestionsId}
255
- aria-haspopup="listbox"
256
- aria-activedescendant={
257
- selectedIndex >= 0 ? `${suggestionsId}-item-${selectedIndex}` : undefined
258
- }
259
- {...props}
260
- />
261
- {isLoading && (
262
- <div className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none">
263
- <LoadingSpinner size="sm" />
264
- </div>
265
- )}
266
- </div>
249
+ <form ref={containerRef} className={cn('relative w-full', className)}>
250
+ <Input
251
+ ref={inputRef}
252
+ type="text"
253
+ value={value}
254
+ onChange={handleInputChange}
255
+ onKeyDown={handleKeyDown}
256
+ onFocus={handleFocus}
257
+ onBlur={handleBlur}
258
+ placeholder={placeholder}
259
+ disabled={disabled}
260
+ error={hasError}
261
+ size={size}
262
+ variant={variant}
263
+ role="combobox"
264
+ aria-expanded={isOpen}
265
+ aria-autocomplete="list"
266
+ aria-controls={suggestionsId}
267
+ aria-haspopup="listbox"
268
+ aria-activedescendant={
269
+ selectedIndex >= 0 ? `${suggestionsId}-item-${selectedIndex}` : undefined
270
+ }
271
+ {...props}
272
+ />
273
+ {isLoading && (
274
+ <p className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none">
275
+ <LoadingSpinner size="sm" />
276
+ </p>
277
+ )}
267
278
 
268
279
  {isOpen && suggestions.length > 0 && (
269
- <ul
280
+ <dl
270
281
  ref={suggestionsRef}
271
282
  id={suggestionsId}
272
283
  role="listbox"
@@ -278,32 +289,32 @@ const AddressField = React.forwardRef<HTMLInputElement, AddressFieldProps>(
278
289
  data-testid="address-suggestions"
279
290
  >
280
291
  {suggestions.map((suggestion, index) => (
281
- <li
282
- key={suggestion.place_id}
283
- id={`${suggestionsId}-item-${index}`}
284
- role="option"
285
- aria-selected={selectedIndex === index}
286
- className={cn(
287
- 'px-3 py-2 cursor-pointer text-sm',
288
- 'hover:bg-main-100 focus:bg-main-100',
289
- 'border-b border-main-200 last:border-b-0',
290
- selectedIndex === index && 'bg-main-100'
291
- )}
292
- onClick={() => handleSelectAddress(suggestion.place_id)}
293
- onMouseEnter={() => setSelectedIndex(index)}
294
- data-testid={`address-suggestion-${index}`}
295
- >
296
- <div className="font-medium text-main-900">
292
+ <React.Fragment key={suggestion.place_id}>
293
+ <dt
294
+ id={`${suggestionsId}-item-${index}`}
295
+ role="option"
296
+ aria-selected={selectedIndex === index}
297
+ className={cn(
298
+ 'px-3 py-2 cursor-pointer text-sm',
299
+ 'hover:bg-main-100 focus:bg-main-100',
300
+ 'border-b border-main-200 last:border-b-0',
301
+ selectedIndex === index && 'bg-main-100',
302
+ 'font-medium text-main-900'
303
+ )}
304
+ onClick={() => handleSelectAddress(suggestion.place_id)}
305
+ onMouseEnter={() => setSelectedIndex(index)}
306
+ data-testid={`address-suggestion-${index}`}
307
+ >
297
308
  {suggestion.structured_formatting?.main_text || suggestion.description}
298
- </div>
309
+ </dt>
299
310
  {suggestion.structured_formatting?.secondary_text && (
300
- <div className="text-xs text-main-600 mt-0.5">
311
+ <dd className="px-3 pb-2 text-xs text-main-600 mt-0.5">
301
312
  {suggestion.structured_formatting.secondary_text}
302
- </div>
313
+ </dd>
303
314
  )}
304
- </li>
315
+ </React.Fragment>
305
316
  ))}
306
- </ul>
317
+ </dl>
307
318
  )}
308
319
 
309
320
  {autocompleteError && (
@@ -311,7 +322,7 @@ const AddressField = React.forwardRef<HTMLInputElement, AddressFieldProps>(
311
322
  {autocompleteError.message}
312
323
  </p>
313
324
  )}
314
- </div>
325
+ </form>
315
326
  );
316
327
  }
317
328
  );
@@ -154,7 +154,7 @@ if (address) {
154
154
 
155
155
  ## ParsedAddress Type
156
156
 
157
- The `onChange` callback receives a `ParsedAddress` object matching the `pace_address` table structure:
157
+ The `onChange` callback receives a `ParsedAddress` object matching the `core_address` table structure:
158
158
 
159
159
  ```typescript
160
160
  interface ParsedAddress {
@@ -202,7 +202,7 @@ The component uses intelligent caching to reduce API costs:
202
202
 
203
203
  The `place_id` is a stable identifier for a location that doesn't change. It's crucial for:
204
204
 
205
- 1. **Storing in Database**: Save `place_id` in your `pace_address` table
205
+ 1. **Storing in Database**: Save `place_id` in your `core_address` table
206
206
  2. **Retrieving Later**: Use `getAddressByPlaceId()` to get full address without autocomplete
207
207
  3. **Verification**: Verify addresses haven't changed
208
208
  4. **Cost Efficiency**: Place details lookups are cheaper than autocomplete searches
@@ -215,7 +215,7 @@ The `place_id` is a stable identifier for a location that doesn't change. It's c
215
215
  apiKey={apiKey}
216
216
  onChange={async (address) => {
217
217
  // Store in database
218
- await supabase.from('pace_address').insert({
218
+ await supabase.from('core_address').insert({
219
219
  place_id: address.place_id, // Store this!
220
220
  full_address: address.full_address,
221
221
  lat: address.lat,
@@ -242,6 +242,7 @@ const address = await getAddressByPlaceId(storedPlaceId, apiKey);
242
242
 
243
243
  The component follows WCAG 2.1 guidelines:
244
244
 
245
+ - **Semantic HTML**: Uses description list (`<dl>`, `<dt>`, `<dd>`) for address suggestions, providing proper semantic meaning
245
246
  - **ARIA Attributes**: Proper `role`, `aria-expanded`, `aria-autocomplete`, `aria-controls`
246
247
  - **Keyboard Navigation**: Full keyboard support
247
248
  - **Screen Readers**: Proper announcements and labels
@@ -256,16 +257,16 @@ The component handles various error scenarios:
256
257
  - **Rate Limiting**: Informs user about quota limits
257
258
  - **Invalid Requests**: Validates input and displays errors
258
259
 
259
- ## Integration with pace_address Table
260
+ ## Integration with core_address Table
260
261
 
261
- The `ParsedAddress` type matches the `pace_address` table structure, making it easy to store results:
262
+ The `ParsedAddress` type matches the `core_address` table structure, making it easy to store results:
262
263
 
263
264
  ```tsx
264
265
  <AddressField
265
266
  apiKey={apiKey}
266
267
  onChange={async (address) => {
267
268
  const { data, error } = await supabase
268
- .from('pace_address')
269
+ .from('core_address')
269
270
  .insert({
270
271
  place_id: address.place_id,
271
272
  full_address: address.full_address,
@@ -11,6 +11,19 @@ import { renderWithProviders } from '../../__tests__/helpers/test-utils';
11
11
 
12
12
  describe('Alert Component', () => {
13
13
  describe('Rendering', () => {
14
+ it('renders as semantic aside element', () => {
15
+ renderWithProviders(
16
+ <Alert>
17
+ <AlertTitle>Test Title</AlertTitle>
18
+ <AlertDescription>Test description</AlertDescription>
19
+ </Alert>
20
+ );
21
+
22
+ const alert = screen.getByRole('alert');
23
+ expect(alert.tagName).toBe('ASIDE');
24
+ expect(alert).toBeInTheDocument();
25
+ });
26
+
14
27
  it('renders with default variant', () => {
15
28
  renderWithProviders(
16
29
  <Alert>
@@ -21,6 +34,7 @@ describe('Alert Component', () => {
21
34
 
22
35
  const alert = screen.getByRole('alert');
23
36
  expect(alert).toBeInTheDocument();
37
+ expect(alert.tagName).toBe('ASIDE');
24
38
  expect(alert).toHaveClass('relative', 'w-full', 'rounded-lg', 'border', 'p-4');
25
39
  });
26
40
 
@@ -34,6 +48,7 @@ describe('Alert Component', () => {
34
48
 
35
49
  const alert = screen.getByRole('alert');
36
50
  expect(alert).toBeInTheDocument();
51
+ expect(alert.tagName).toBe('ASIDE');
37
52
  expect(alert).toHaveClass('border-destructive', 'text-destructive');
38
53
  });
39
54
 
@@ -58,6 +73,7 @@ describe('Alert Component', () => {
58
73
  );
59
74
 
60
75
  const alert = screen.getByRole('alert');
76
+ expect(alert.tagName).toBe('ASIDE');
61
77
  expect(alert).toHaveClass('custom-alert-class');
62
78
  });
63
79
 
@@ -72,7 +88,7 @@ describe('Alert Component', () => {
72
88
  });
73
89
 
74
90
  it('forwards ref correctly', () => {
75
- const ref = React.createRef<HTMLDivElement>();
91
+ const ref = React.createRef<HTMLElement>();
76
92
 
77
93
  renderWithProviders(
78
94
  <Alert ref={ref}>
@@ -80,7 +96,8 @@ describe('Alert Component', () => {
80
96
  </Alert>
81
97
  );
82
98
 
83
- expect(ref.current).toBeInstanceOf(HTMLDivElement);
99
+ expect(ref.current).toBeInstanceOf(HTMLElement);
100
+ expect(ref.current?.tagName).toBe('ASIDE');
84
101
  expect(ref.current).toHaveAttribute('role', 'alert');
85
102
  });
86
103
  });
@@ -258,6 +275,7 @@ describe('Alert Component', () => {
258
275
 
259
276
  const alert = screen.getByRole('alert');
260
277
  expect(alert).toBeInTheDocument();
278
+ expect(alert.tagName).toBe('ASIDE');
261
279
  });
262
280
 
263
281
  it('does not have role="alert" for inline variant', () => {
@@ -283,6 +301,7 @@ describe('Alert Component', () => {
283
301
 
284
302
  const alert = screen.getByRole('alert');
285
303
  expect(alert).toBeInTheDocument();
304
+ expect(alert.tagName).toBe('ASIDE');
286
305
 
287
306
  // Screen readers will announce the content within the alert
288
307
  expect(screen.getByText('Important Notice')).toBeInTheDocument();
@@ -297,9 +316,11 @@ describe('Alert Component', () => {
297
316
  </Alert>
298
317
  );
299
318
 
319
+ const alert = screen.getByRole('alert');
300
320
  const title = screen.getByRole('heading', { level: 5 });
301
321
  const description = screen.getByText('Semantic description with proper heading structure');
302
322
 
323
+ expect(alert.tagName).toBe('ASIDE');
303
324
  expect(title).toBeInTheDocument();
304
325
  expect(description.tagName).toBe('P');
305
326
  });
@@ -320,7 +341,9 @@ describe('Alert Component', () => {
320
341
  </Alert>
321
342
  );
322
343
 
323
- expect(screen.getByRole('alert')).toBeInTheDocument();
344
+ const alert = screen.getByRole('alert');
345
+ expect(alert).toBeInTheDocument();
346
+ expect(alert.tagName).toBe('ASIDE');
324
347
  expect(screen.getByText('⚠️')).toBeInTheDocument();
325
348
  expect(screen.getByRole('button', { name: 'Dismiss' })).toBeInTheDocument();
326
349
  });
@@ -334,7 +357,9 @@ describe('Alert Component', () => {
334
357
  </Alert>
335
358
  );
336
359
 
337
- expect(screen.getByRole('alert')).toBeInTheDocument();
360
+ const alert = screen.getByRole('alert');
361
+ expect(alert).toBeInTheDocument();
362
+ expect(alert.tagName).toBe('ASIDE');
338
363
  expect(screen.getByText('First description')).toBeInTheDocument();
339
364
  expect(screen.getByText('Second description')).toBeInTheDocument();
340
365
  });
@@ -346,7 +371,9 @@ describe('Alert Component', () => {
346
371
  </Alert>
347
372
  );
348
373
 
349
- expect(screen.getByRole('alert')).toBeInTheDocument();
374
+ const alert = screen.getByRole('alert');
375
+ expect(alert).toBeInTheDocument();
376
+ expect(alert.tagName).toBe('ASIDE');
350
377
  expect(screen.getByText('Description without title')).toBeInTheDocument();
351
378
  });
352
379
 
@@ -357,7 +384,9 @@ describe('Alert Component', () => {
357
384
  </Alert>
358
385
  );
359
386
 
360
- expect(screen.getByRole('alert')).toBeInTheDocument();
387
+ const alert = screen.getByRole('alert');
388
+ expect(alert).toBeInTheDocument();
389
+ expect(alert.tagName).toBe('ASIDE');
361
390
  expect(screen.getByRole('heading', { level: 5 })).toHaveTextContent('Title without description');
362
391
  });
363
392
  });
@@ -368,6 +397,7 @@ describe('Alert Component', () => {
368
397
 
369
398
  const alert = screen.getByRole('alert');
370
399
  expect(alert).toBeInTheDocument();
400
+ expect(alert.tagName).toBe('ASIDE');
371
401
  expect(alert).toBeEmptyDOMElement();
372
402
  });
373
403
 
@@ -395,7 +425,9 @@ describe('Alert Component', () => {
395
425
  );
396
426
 
397
427
  // Should fallback to default behavior
398
- expect(screen.getByRole('alert')).toBeInTheDocument();
428
+ const alert = screen.getByRole('alert');
429
+ expect(alert).toBeInTheDocument();
430
+ expect(alert.tagName).toBe('ASIDE');
399
431
  });
400
432
 
401
433
  it('handles rapid variant changes', () => {
@@ -424,7 +456,9 @@ describe('Alert Component', () => {
424
456
  </Alert>
425
457
  );
426
458
 
427
- expect(screen.getByRole('alert')).toBeInTheDocument();
459
+ const alert = screen.getByRole('alert');
460
+ expect(alert).toBeInTheDocument();
461
+ expect(alert.tagName).toBe('ASIDE');
428
462
  });
429
463
  });
430
464
 
@@ -441,7 +475,9 @@ describe('Alert Component', () => {
441
475
  </form>
442
476
  );
443
477
 
444
- expect(screen.getByRole('alert')).toBeInTheDocument();
478
+ const alert = screen.getByRole('alert');
479
+ expect(alert).toBeInTheDocument();
480
+ expect(alert.tagName).toBe('ASIDE');
445
481
  expect(screen.getByRole('textbox')).toBeInTheDocument();
446
482
  expect(screen.getByRole('button', { name: 'Submit' })).toBeInTheDocument();
447
483
  });
@@ -462,6 +498,8 @@ describe('Alert Component', () => {
462
498
 
463
499
  const alerts = screen.getAllByRole('alert');
464
500
  expect(alerts).toHaveLength(2);
501
+ expect(alerts[0].tagName).toBe('ASIDE');
502
+ expect(alerts[1].tagName).toBe('ASIDE');
465
503
  expect(alerts[0]).toHaveClass('bg-background', 'text-foreground');
466
504
  expect(alerts[1]).toHaveClass('border-destructive', 'text-destructive');
467
505
  });
@@ -482,7 +520,9 @@ describe('Alert Component', () => {
482
520
  </Alert>
483
521
  );
484
522
 
485
- expect(screen.getByRole('alert')).toBeInTheDocument();
523
+ const alert = screen.getByRole('alert');
524
+ expect(alert).toBeInTheDocument();
525
+ expect(alert.tagName).toBe('ASIDE');
486
526
  expect(screen.getByText('Complex Alert')).toBeInTheDocument();
487
527
  expect(screen.getByText('This is a complex description with')).toBeInTheDocument();
488
528
  expect(screen.getByText('Multiple elements')).toBeInTheDocument();
@@ -10,6 +10,7 @@
10
10
  * Features:
11
11
  * - Multiple visual variants (default, destructive, inline)
12
12
  * - Title and description support
13
+ * - Semantic HTML: renders as `<aside>` element
13
14
  * - ARIA role="alert" for accessibility
14
15
  * - Keyboard and screen reader accessible
15
16
  * - Composable with icons and actions
@@ -40,6 +41,7 @@
40
41
  * ```
41
42
  *
42
43
  * @accessibility
44
+ * - Uses semantic HTML: `<aside>` element for better semantic meaning
43
45
  * - Uses role="alert" for screen reader announcement
44
46
  * - Title and description are semantically structured
45
47
  * - Supports keyboard navigation and focus
@@ -65,8 +67,8 @@ const getAlertClasses = (variant: "default" | "destructive" | "inline" = "defaul
65
67
  };
66
68
 
67
69
  const Alert = React.forwardRef<
68
- HTMLDivElement,
69
- React.HTMLAttributes<HTMLDivElement> & { variant?: "default" | "destructive" | "inline" }
70
+ HTMLElement,
71
+ React.HTMLAttributes<HTMLElement> & { variant?: "default" | "destructive" | "inline" }
70
72
  >(({ className, variant = "default", ...props }, ref) => {
71
73
  const contextValue = React.useMemo(() => ({ variant }), [variant])
72
74
 
@@ -80,7 +82,7 @@ const Alert = React.forwardRef<
80
82
 
81
83
  return (
82
84
  <AlertContext.Provider value={contextValue}>
83
- <div
85
+ <aside
84
86
  ref={ref}
85
87
  className={cn(getAlertClasses(variant), className)}
86
88
  role="alert"