@jmruthers/pace-core 0.5.191 → 0.5.193

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 (293) hide show
  1. package/dist/{AuthService-CbP_utw2.d.ts → AuthService-DjnJHDtC.d.ts} +1 -0
  2. package/dist/{DataTable-WKRZD47S.js → DataTable-5FU7IESH.js} +7 -6
  3. package/dist/{PublicPageProvider-ULXC_u6U.d.ts → PublicPageProvider-C0Sm_e5k.d.ts} +3 -1
  4. package/dist/{UnifiedAuthProvider-BYA9qB-o.d.ts → UnifiedAuthProvider-185Ih4dj.d.ts} +2 -0
  5. package/dist/{UnifiedAuthProvider-FTSG5XH7.js → UnifiedAuthProvider-RGJTDE2C.js} +3 -3
  6. package/dist/{api-IHKALJZD.js → api-N774RPUA.js} +2 -2
  7. package/dist/chunk-6C4YBBJM 5.js +628 -0
  8. package/dist/chunk-7D4SUZUM.js 2.map +1 -0
  9. package/dist/{chunk-LOMZXPSN.js → chunk-7EQTDTTJ.js} +47 -74
  10. package/dist/chunk-7EQTDTTJ.js 2.map +1 -0
  11. package/dist/chunk-7EQTDTTJ.js.map +1 -0
  12. package/dist/{chunk-6LTQQAT6.js → chunk-7FLMSG37.js} +336 -137
  13. package/dist/chunk-7FLMSG37.js 2.map +1 -0
  14. package/dist/chunk-7FLMSG37.js.map +1 -0
  15. package/dist/{chunk-XNYQOL3Z.js → chunk-BC4IJKSL.js} +9 -18
  16. package/dist/chunk-BC4IJKSL.js.map +1 -0
  17. package/dist/{chunk-ULHIJK66.js → chunk-E3SPN4VZ 5.js } +146 -36
  18. package/dist/chunk-E3SPN4VZ.js +12917 -0
  19. package/dist/{chunk-ULHIJK66.js.map → chunk-E3SPN4VZ.js.map} +1 -1
  20. package/dist/chunk-E66EQZE6 5.js +37 -0
  21. package/dist/chunk-E66EQZE6.js 2.map +1 -0
  22. package/dist/{chunk-6TQDD426.js → chunk-HWIIPPNI.js} +40 -221
  23. package/dist/chunk-HWIIPPNI.js.map +1 -0
  24. package/dist/chunk-I7PSE6JW 5.js +191 -0
  25. package/dist/chunk-I7PSE6JW.js 2.map +1 -0
  26. package/dist/{chunk-OETXORNB.js → chunk-IIELH4DL.js} +211 -136
  27. package/dist/chunk-IIELH4DL.js.map +1 -0
  28. package/dist/{chunk-ROXMHMY2.js → chunk-KNC55RTG.js} +13 -3
  29. package/dist/{chunk-ROXMHMY2.js.map → chunk-KNC55RTG.js 5.map } +1 -1
  30. package/dist/chunk-KNC55RTG.js.map +1 -0
  31. package/dist/chunk-KQCRWDSA.js 5.map +1 -0
  32. package/dist/{chunk-XYXSXPUK.js → chunk-LFNCN2SP.js} +7 -6
  33. package/dist/chunk-LFNCN2SP.js 2.map +1 -0
  34. package/dist/chunk-LFNCN2SP.js.map +1 -0
  35. package/dist/chunk-LMC26NLJ 2.js +84 -0
  36. package/dist/{chunk-VKB2CO4Z.js → chunk-NOAYCWCX 5.js } +84 -87
  37. package/dist/chunk-NOAYCWCX.js +4993 -0
  38. package/dist/chunk-NOAYCWCX.js.map +1 -0
  39. package/dist/chunk-QWWZ5CAQ.js 3.map +1 -0
  40. package/dist/chunk-QXHPKYJV 3.js +113 -0
  41. package/dist/chunk-R77UEZ4E 3.js +68 -0
  42. package/dist/chunk-VBXEHIUJ.js 6.map +1 -0
  43. package/dist/{chunk-VRGWKHDB.js → chunk-XNXXZ43G.js} +77 -33
  44. package/dist/chunk-XNXXZ43G.js.map +1 -0
  45. package/dist/chunk-ZSAAAMVR 6.js +25 -0
  46. package/dist/components.d.ts +2 -2
  47. package/dist/components.js +7 -7
  48. package/dist/components.js 5.map +1 -0
  49. package/dist/hooks.js +8 -8
  50. package/dist/index.d.ts +5 -5
  51. package/dist/index.js +12 -14
  52. package/dist/index.js.map +1 -1
  53. package/dist/providers.d.ts +3 -3
  54. package/dist/providers.js +2 -2
  55. package/dist/rbac/index.d.ts +1 -19
  56. package/dist/rbac/index.js +7 -9
  57. package/dist/styles/index 2.js +12 -0
  58. package/dist/styles/index.js 5.map +1 -0
  59. package/dist/theming/runtime 5.js +19 -0
  60. package/dist/theming/runtime.js 5.map +1 -0
  61. package/dist/utils.js +1 -1
  62. package/docs/api/classes/ColumnFactory.md +1 -1
  63. package/docs/api/classes/ErrorBoundary.md +1 -1
  64. package/docs/api/classes/InvalidScopeError.md +1 -1
  65. package/docs/api/classes/Logger.md +1 -1
  66. package/docs/api/classes/MissingUserContextError.md +1 -1
  67. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  68. package/docs/api/classes/PermissionDeniedError.md +2 -2
  69. package/docs/api/classes/RBACAuditManager.md +2 -2
  70. package/docs/api/classes/RBACCache.md +1 -1
  71. package/docs/api/classes/RBACEngine.md +2 -2
  72. package/docs/api/classes/RBACError.md +1 -1
  73. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  74. package/docs/api/classes/SecureSupabaseClient.md +10 -10
  75. package/docs/api/classes/StorageUtils.md +1 -1
  76. package/docs/api/enums/FileCategory.md +1 -1
  77. package/docs/api/enums/LogLevel.md +1 -1
  78. package/docs/api/enums/RBACErrorCode.md +1 -1
  79. package/docs/api/enums/RPCFunction.md +1 -1
  80. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  81. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  82. package/docs/api/interfaces/AggregateConfig.md +1 -1
  83. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  84. package/docs/api/interfaces/AvatarProps.md +1 -1
  85. package/docs/api/interfaces/BadgeProps.md +1 -1
  86. package/docs/api/interfaces/ButtonProps.md +1 -1
  87. package/docs/api/interfaces/CalendarProps.md +1 -1
  88. package/docs/api/interfaces/CardProps.md +1 -1
  89. package/docs/api/interfaces/ColorPalette.md +1 -1
  90. package/docs/api/interfaces/ColorShade.md +1 -1
  91. package/docs/api/interfaces/ComplianceResult.md +1 -1
  92. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  93. package/docs/api/interfaces/DataRecord.md +1 -1
  94. package/docs/api/interfaces/DataTableAction.md +1 -1
  95. package/docs/api/interfaces/DataTableColumn.md +1 -1
  96. package/docs/api/interfaces/DataTableProps.md +1 -1
  97. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  98. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  99. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  100. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  101. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  102. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  103. package/docs/api/interfaces/ExportColumn.md +1 -1
  104. package/docs/api/interfaces/ExportOptions.md +1 -1
  105. package/docs/api/interfaces/FileDisplayProps.md +24 -11
  106. package/docs/api/interfaces/FileMetadata.md +1 -1
  107. package/docs/api/interfaces/FileReference.md +1 -1
  108. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  109. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  110. package/docs/api/interfaces/FileUploadProps.md +1 -1
  111. package/docs/api/interfaces/FooterProps.md +1 -1
  112. package/docs/api/interfaces/FormFieldProps.md +1 -1
  113. package/docs/api/interfaces/FormProps.md +1 -1
  114. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  115. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  116. package/docs/api/interfaces/InputProps.md +1 -1
  117. package/docs/api/interfaces/LabelProps.md +1 -1
  118. package/docs/api/interfaces/LoggerConfig.md +1 -1
  119. package/docs/api/interfaces/LoginFormProps.md +1 -1
  120. package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
  121. package/docs/api/interfaces/NavigationContextType.md +1 -1
  122. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  123. package/docs/api/interfaces/NavigationItem.md +1 -1
  124. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  125. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  126. package/docs/api/interfaces/Organisation.md +1 -1
  127. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  128. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  129. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  130. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  131. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  132. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  133. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  134. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  135. package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
  136. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  137. package/docs/api/interfaces/PaletteData.md +1 -1
  138. package/docs/api/interfaces/ParsedAddress.md +1 -1
  139. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  140. package/docs/api/interfaces/ProgressProps.md +1 -1
  141. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  142. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  143. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  144. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  145. package/docs/api/interfaces/QuickFix.md +1 -1
  146. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  147. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  148. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  149. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  150. package/docs/api/interfaces/RBACConfig.md +2 -2
  151. package/docs/api/interfaces/RBACContext.md +1 -1
  152. package/docs/api/interfaces/RBACLogger.md +1 -1
  153. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  154. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  155. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  156. package/docs/api/interfaces/RBACPermissionCheckResult.md +2 -2
  157. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  158. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  159. package/docs/api/interfaces/RBACResult.md +1 -1
  160. package/docs/api/interfaces/RBACRoleGrantParams.md +2 -2
  161. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  162. package/docs/api/interfaces/RBACRoleRevokeParams.md +2 -2
  163. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  164. package/docs/api/interfaces/RBACRoleValidateParams.md +2 -2
  165. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  166. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  167. package/docs/api/interfaces/RBACRolesListResult.md +2 -2
  168. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  169. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  170. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  171. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  172. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  173. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  174. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  175. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  176. package/docs/api/interfaces/RouteConfig.md +2 -2
  177. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  178. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  179. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  180. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  181. package/docs/api/interfaces/SetupIssue.md +1 -1
  182. package/docs/api/interfaces/StorageConfig.md +1 -1
  183. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  184. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  185. package/docs/api/interfaces/StorageListOptions.md +1 -1
  186. package/docs/api/interfaces/StorageListResult.md +1 -1
  187. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  188. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  189. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  190. package/docs/api/interfaces/StyleImport.md +1 -1
  191. package/docs/api/interfaces/SwitchProps.md +1 -1
  192. package/docs/api/interfaces/TabsContentProps.md +1 -1
  193. package/docs/api/interfaces/TabsListProps.md +1 -1
  194. package/docs/api/interfaces/TabsProps.md +1 -1
  195. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  196. package/docs/api/interfaces/TextareaProps.md +1 -1
  197. package/docs/api/interfaces/ToastActionElement.md +1 -1
  198. package/docs/api/interfaces/ToastProps.md +1 -1
  199. package/docs/api/interfaces/UnifiedAuthContextType.md +60 -38
  200. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  201. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  202. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  203. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  204. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  205. package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
  206. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  207. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  208. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  209. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
  210. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  211. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  212. package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
  213. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  214. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  215. package/docs/api/interfaces/UserEventAccess.md +1 -1
  216. package/docs/api/interfaces/UserMenuProps.md +1 -1
  217. package/docs/api/interfaces/UserProfile.md +1 -1
  218. package/docs/api/modules.md +194 -209
  219. package/docs/migration/database-changes-december-2025.md +2 -1
  220. package/docs/rbac/event-based-apps.md +124 -6
  221. package/package.json +1 -1
  222. package/scripts/check-pace-core-compliance.cjs +292 -57
  223. package/src/__tests__/rls-policies.test.ts +3 -1
  224. package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +172 -45
  225. package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +121 -28
  226. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +9 -8
  227. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +20 -52
  228. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +170 -34
  229. package/src/components/DataTable/__tests__/keyboard.test.tsx +75 -12
  230. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +75 -11
  231. package/src/components/DataTable/components/UnifiedTableBody.tsx +85 -14
  232. package/src/components/DataTable/hooks/useDataTablePermissions.ts +75 -10
  233. package/src/components/FileDisplay/FileDisplay.test.tsx +2 -1
  234. package/src/components/FileDisplay/FileDisplay.tsx +16 -4
  235. package/src/components/NavigationMenu/NavigationMenu.test.tsx +6 -4
  236. package/src/components/NavigationMenu/NavigationMenu.tsx +1 -10
  237. package/src/components/OrganisationSelector/OrganisationSelector.tsx +0 -1
  238. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +25 -2
  239. package/src/components/PaceAppLayout/PaceAppLayout.tsx +97 -68
  240. package/src/components/PaceLoginPage/PaceLoginPage.tsx +0 -7
  241. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +5 -9
  242. package/src/components/ProtectedRoute/ProtectedRoute.tsx +0 -1
  243. package/src/components/PublicLayout/PublicPageProvider.tsx +0 -1
  244. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +14 -7
  245. package/src/hooks/services/useAuthService.ts +21 -3
  246. package/src/hooks/services/useEventService.ts +21 -3
  247. package/src/hooks/services/useInactivityService.ts +21 -3
  248. package/src/hooks/services/useOrganisationService.ts +21 -3
  249. package/src/hooks/useFileDisplay.ts +10 -17
  250. package/src/hooks/useSecureDataAccess.test.ts +16 -9
  251. package/src/hooks/useSecureDataAccess.ts +3 -2
  252. package/src/providers/services/EventServiceProvider.tsx +0 -8
  253. package/src/providers/services/UnifiedAuthProvider.tsx +174 -24
  254. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +10 -16
  255. package/src/rbac/__tests__/isSuperAdmin.real.test.ts +82 -0
  256. package/src/rbac/adapters.tsx +3 -22
  257. package/src/rbac/api.test.ts +2 -2
  258. package/src/rbac/api.ts +7 -1
  259. package/src/rbac/components/EnhancedNavigationMenu.tsx +2 -15
  260. package/src/rbac/components/NavigationGuard.tsx +1 -10
  261. package/src/rbac/components/NavigationProvider.tsx +0 -1
  262. package/src/rbac/components/PermissionEnforcer.tsx +45 -12
  263. package/src/rbac/components/SecureDataProvider.tsx +0 -1
  264. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +7 -43
  265. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +4 -11
  266. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +3 -3
  267. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +1 -1
  268. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +1 -1
  269. package/src/rbac/engine.ts +14 -2
  270. package/src/rbac/hooks/index.ts +0 -3
  271. package/src/rbac/hooks/usePermissions.ts +51 -11
  272. package/src/rbac/hooks/useRBAC.ts +3 -13
  273. package/src/rbac/hooks/useResolvedScope.test.ts +75 -54
  274. package/src/rbac/hooks/useResolvedScope.ts +58 -33
  275. package/src/rbac/hooks/useSecureSupabase.ts +4 -9
  276. package/src/rbac/secureClient.ts +31 -0
  277. package/src/services/EventService.ts +4 -57
  278. package/src/services/InactivityService.ts +127 -34
  279. package/src/services/OrganisationService.ts +68 -10
  280. package/dist/chunk-6LTQQAT6.js.map +0 -1
  281. package/dist/chunk-6TQDD426.js.map +0 -1
  282. package/dist/chunk-LOMZXPSN.js.map +0 -1
  283. package/dist/chunk-OETXORNB.js.map +0 -1
  284. package/dist/chunk-VKB2CO4Z.js.map +0 -1
  285. package/dist/chunk-VRGWKHDB.js.map +0 -1
  286. package/dist/chunk-XNYQOL3Z.js.map +0 -1
  287. package/dist/chunk-XYXSXPUK.js.map +0 -1
  288. package/scripts/check-pace-core-compliance.js +0 -512
  289. package/src/rbac/hooks/useSuperAdminBypass.ts +0 -126
  290. package/src/utils/context/superAdminOverride.ts +0 -58
  291. /package/dist/{DataTable-WKRZD47S.js.map → DataTable-5FU7IESH.js.map} +0 -0
  292. /package/dist/{UnifiedAuthProvider-FTSG5XH7.js.map → UnifiedAuthProvider-RGJTDE2C.js.map} +0 -0
  293. /package/dist/{api-IHKALJZD.js.map → api-N774RPUA.js.map} +0 -0
@@ -1,512 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Static Analysis Script for pace-core Compliance
5
- * @package @jmruthers/pace-core
6
- * @module Scripts/check-pace-core-compliance
7
- *
8
- * Scans a consuming app's codebase to check compliance with pace-core usage.
9
- * Generates a report of violations and suggestions.
10
- */
11
-
12
- const fs = require('fs');
13
- const path = require('path');
14
-
15
- // ANSI color codes for terminal output
16
- const colors = {
17
- reset: '\x1b[0m',
18
- red: '\x1b[31m',
19
- green: '\x1b[32m',
20
- yellow: '\x1b[33m',
21
- blue: '\x1b[34m',
22
- cyan: '\x1b[36m',
23
- bold: '\x1b[1m'
24
- };
25
-
26
- // Load manifest
27
- function loadManifest() {
28
- const manifestPath = path.join(__dirname, '../core-usage-manifest.json');
29
- if (!fs.existsSync(manifestPath)) {
30
- console.error(`${colors.red}Error: core-usage-manifest.json not found at ${manifestPath}${colors.reset}`);
31
- process.exit(1);
32
- }
33
- return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
34
- }
35
-
36
- // Find project root (look for package.json, going up from current dir or script location)
37
- function findProjectRoot(startDir = process.cwd()) {
38
- let current = path.resolve(startDir);
39
- while (current !== path.dirname(current)) {
40
- if (fs.existsSync(path.join(current, 'package.json'))) {
41
- return current;
42
- }
43
- current = path.dirname(current);
44
- }
45
- return startDir;
46
- }
47
-
48
- // Scan file for violations
49
- function scanFile(filePath, manifest) {
50
- const violations = {
51
- restrictedImports: [],
52
- duplicateComponents: [],
53
- duplicateHooks: [],
54
- duplicateUtils: [],
55
- suggestions: [],
56
- customAuthCode: [],
57
- duplicateConfig: [],
58
- unprotectedPages: [],
59
- directSupabaseAuth: []
60
- };
61
-
62
- const content = fs.readFileSync(filePath, 'utf8');
63
- const relativePath = path.relative(process.cwd(), filePath);
64
-
65
- // Check for restricted imports
66
- manifest.restrictedImports.forEach(({ module, reason }) => {
67
- const importPattern = new RegExp(`from\\s+['"]${module.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"]`, 'g');
68
- if (importPattern.test(content)) {
69
- violations.restrictedImports.push({
70
- module,
71
- reason,
72
- file: relativePath,
73
- line: getLineNumber(content, content.match(importPattern)[0])
74
- });
75
- }
76
-
77
- // Also check for @radix-ui/* pattern
78
- if (module.startsWith('@radix-ui/')) {
79
- const radixPattern = /from\s+['"]@radix-ui\/[^'"]+['"]/g;
80
- const matches = content.match(radixPattern);
81
- if (matches) {
82
- matches.forEach(match => {
83
- const matchedModule = match.match(/['"]([^'"]+)['"]/)[1];
84
- if (!manifest.restrictedImports.find(ri => ri.module === matchedModule)) {
85
- violations.restrictedImports.push({
86
- module: matchedModule,
87
- reason: 'Use pace-core component instead of direct Radix UI import',
88
- file: relativePath,
89
- line: getLineNumber(content, match)
90
- });
91
- }
92
- });
93
- }
94
- }
95
- });
96
-
97
- // Check for duplicate component names
98
- const filename = path.basename(filePath);
99
- const componentName = filename.replace(/\.(tsx?|jsx?)$/, '').replace(/\.(test|spec)$/, '');
100
-
101
- if (manifest.components.includes(componentName)) {
102
- // Check if this file exports a component
103
- if (content.match(/export\s+(default\s+)?(function|const|class)\s+(\w+)?/)) {
104
- violations.duplicateComponents.push({
105
- component: componentName,
106
- file: relativePath
107
- });
108
- }
109
- }
110
-
111
- // Check for duplicate hook names
112
- if (filename.startsWith('use') && filename.endsWith('.ts') || filename.endsWith('.tsx')) {
113
- const hookName = filename.replace(/\.(tsx?|jsx?)$/, '').replace(/\.(test|spec)$/, '');
114
- if (manifest.hooks.includes(hookName)) {
115
- if (content.match(/export\s+(default\s+)?(function|const)\s+(\w+)?/)) {
116
- violations.duplicateHooks.push({
117
- hook: hookName,
118
- file: relativePath
119
- });
120
- }
121
- }
122
- }
123
-
124
- // Check for duplicate util names
125
- const utilName = filename.replace(/\.(ts|js)$/, '').replace(/\.(test|spec)$/, '');
126
- if (manifest.utils.includes(utilName)) {
127
- if (content.match(/export\s+(default\s+)?(function|const)\s+(\w+)?/)) {
128
- violations.duplicateUtils.push({
129
- util: utilName,
130
- file: relativePath
131
- });
132
- }
133
- }
134
-
135
- // Check for native HTML elements that should use pace-core components
136
- const nativeElementPatterns = {
137
- '<button': { suggestion: 'Use Button from @jmruthers/pace-core' },
138
- '<input': { suggestion: 'Use Input from @jmruthers/pace-core' },
139
- '<textarea': { suggestion: 'Use Textarea from @jmruthers/pace-core' },
140
- '<label': { suggestion: 'Use Label from @jmruthers/pace-core' }
141
- };
142
-
143
- Object.entries(nativeElementPatterns).forEach(([pattern, { suggestion }]) => {
144
- if (content.includes(pattern) && !content.includes('from \'@jmruthers/pace-core\'')) {
145
- violations.suggestions.push({
146
- type: 'native-element',
147
- suggestion,
148
- file: relativePath,
149
- pattern
150
- });
151
- }
152
- });
153
-
154
- // ============================================
155
- // RBAC/Auth Compliance Checks
156
- // ============================================
157
-
158
- // Check for custom auth/rbac/permission code that doesn't import from pace-core
159
- const authRbacPatterns = [
160
- // Custom auth hooks
161
- { pattern: /export\s+(default\s+)?(function|const)\s+useAuth\s*[=\(]/g, name: 'useAuth', type: 'hook' },
162
- { pattern: /export\s+(default\s+)?(function|const)\s+useLogin\s*[=\(]/g, name: 'useLogin', type: 'hook' },
163
- { pattern: /export\s+(default\s+)?(function|const)\s+useLogout\s*[=\(]/g, name: 'useLogout', type: 'hook' },
164
- { pattern: /export\s+(default\s+)?(function|const)\s+useSession\s*[=\(]/g, name: 'useSession', type: 'hook' },
165
- { pattern: /export\s+(default\s+)?(function|const)\s+useUser\s*[=\(]/g, name: 'useUser', type: 'hook' },
166
- { pattern: /export\s+(default\s+)?(function|const)\s+useAuthentication\s*[=\(]/g, name: 'useAuthentication', type: 'hook' },
167
- // Custom RBAC hooks
168
- { pattern: /export\s+(default\s+)?(function|const)\s+usePermissions\s*[=\(]/g, name: 'usePermissions', type: 'hook' },
169
- { pattern: /export\s+(default\s+)?(function|const)\s+useCan\s*[=\(]/g, name: 'useCan', type: 'hook' },
170
- { pattern: /export\s+(default\s+)?(function|const)\s+useAccessLevel\s*[=\(]/g, name: 'useAccessLevel', type: 'hook' },
171
- { pattern: /export\s+(default\s+)?(function|const)\s+useRole\s*[=\(]/g, name: 'useRole', type: 'hook' },
172
- // Custom RBAC components
173
- { pattern: /export\s+(default\s+)?(function|const)\s+PermissionGuard\s*[=\(]/g, name: 'PermissionGuard', type: 'component' },
174
- { pattern: /export\s+(default\s+)?(function|const)\s+AuthGuard\s*[=\(]/g, name: 'AuthGuard', type: 'component' },
175
- { pattern: /export\s+(default\s+)?(function|const)\s+RoleGuard\s*[=\(]/g, name: 'RoleGuard', type: 'component' },
176
- { pattern: /export\s+(default\s+)?(function|const)\s+AccessGuard\s*[=\(]/g, name: 'AccessGuard', type: 'component' },
177
- // Custom permission utilities
178
- { pattern: /export\s+(default\s+)?(function|const)\s+checkPermission\s*[=\(]/g, name: 'checkPermission', type: 'util' },
179
- { pattern: /export\s+(default\s+)?(function|const)\s+hasPermission\s*[=\(]/g, name: 'hasPermission', type: 'util' },
180
- { pattern: /export\s+(default\s+)?(function|const)\s+hasAccess\s*[=\(]/g, name: 'hasAccess', type: 'util' },
181
- { pattern: /export\s+(default\s+)?(function|const)\s+canAccess\s*[=\(]/g, name: 'canAccess', type: 'util' },
182
- { pattern: /export\s+(default\s+)?(function|const)\s+isPermitted\s*[=\(]/g, name: 'isPermitted', type: 'util' }
183
- ];
184
-
185
- // Check if file imports from pace-core for auth/rbac
186
- const hasPaceCoreImport = /from\s+['"]@jmruthers\/pace-core/.test(content) ||
187
- /from\s+['"]@jmruthers\/pace-core\/rbac/.test(content) ||
188
- /from\s+['"]@jmruthers\/pace-core\/providers/.test(content);
189
-
190
- authRbacPatterns.forEach(({ pattern, name, type }) => {
191
- if (pattern.test(content) && !hasPaceCoreImport) {
192
- violations.customAuthCode.push({
193
- name,
194
- type,
195
- file: relativePath,
196
- line: getLineNumber(content, content.match(pattern)[0]),
197
- reason: `Custom ${type} '${name}' detected. Use pace-core's ${name} instead.`
198
- });
199
- }
200
- });
201
-
202
- // Check for duplicate Supabase client configurations
203
- const supabaseCreateClientPattern = /createClient\s*\(/g;
204
- const supabaseCreateClientMatches = content.match(supabaseCreateClientPattern);
205
- if (supabaseCreateClientMatches && supabaseCreateClientMatches.length > 1) {
206
- violations.duplicateConfig.push({
207
- type: 'supabase-client',
208
- file: relativePath,
209
- count: supabaseCreateClientMatches.length,
210
- reason: `Multiple Supabase client instantiations found (${supabaseCreateClientMatches.length}). Consolidate to a single client configuration.`
211
- });
212
- }
213
-
214
- // Check for Supabase URL/key configuration in multiple places
215
- const supabaseUrlPattern = /(SUPABASE_URL|VITE_SUPABASE_URL|NEXT_PUBLIC_SUPABASE_URL|REACT_APP_SUPABASE_URL)/g;
216
- const supabaseKeyPattern = /(SUPABASE_ANON_KEY|VITE_SUPABASE_PUBLISHABLE_KEY|NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY|REACT_APP_SUPABASE_PUBLISHABLE_KEY)/g;
217
- const urlMatches = content.match(supabaseUrlPattern);
218
- const keyMatches = content.match(supabaseKeyPattern);
219
-
220
- if ((urlMatches && urlMatches.length > 2) || (keyMatches && keyMatches.length > 2)) {
221
- violations.duplicateConfig.push({
222
- type: 'supabase-env',
223
- file: relativePath,
224
- reason: 'Supabase environment variables referenced multiple times. Consider centralizing configuration.'
225
- });
226
- }
227
-
228
- // Check for unprotected pages/routes
229
- // Look for route definitions without PagePermissionGuard
230
- const routePatterns = [
231
- /<Route\s+path=["'][^"']+["']/g,
232
- /<Route\s+element\s*=/g,
233
- /createBrowserRouter\s*\(/g,
234
- /createRoutesFromElements/g
235
- ];
236
-
237
- const isRouteFile = routePatterns.some(pattern => pattern.test(content));
238
- const hasPagePermissionGuard = /PagePermissionGuard/.test(content) ||
239
- /from\s+['"]@jmruthers\/pace-core\/rbac['"]/.test(content);
240
-
241
- if (isRouteFile && !hasPagePermissionGuard && !relativePath.includes('test') && !relativePath.includes('spec')) {
242
- violations.unprotectedPages.push({
243
- file: relativePath,
244
- reason: 'Route file found without PagePermissionGuard. All routes should be protected with PagePermissionGuard from pace-core.'
245
- });
246
- }
247
-
248
- // Check for direct Supabase auth usage (should use UnifiedAuthProvider)
249
- const directSupabaseAuthPatterns = [
250
- /\.auth\.signIn/g,
251
- /\.auth\.signUp/g,
252
- /\.auth\.signOut/g,
253
- /\.auth\.getSession/g,
254
- /\.auth\.getUser/g,
255
- /supabase\.auth\./g
256
- ];
257
-
258
- const hasUnifiedAuthImport = /UnifiedAuthProvider/.test(content) ||
259
- /useUnifiedAuth/.test(content) ||
260
- /from\s+['"]@jmruthers\/pace-core\/providers/.test(content);
261
-
262
- directSupabaseAuthPatterns.forEach(pattern => {
263
- if (pattern.test(content) && !hasUnifiedAuthImport && !hasPaceCoreImport) {
264
- violations.directSupabaseAuth.push({
265
- file: relativePath,
266
- line: getLineNumber(content, content.match(pattern)[0]),
267
- reason: 'Direct Supabase auth usage detected. Use UnifiedAuthProvider and useUnifiedAuth from pace-core instead.'
268
- });
269
- }
270
- });
271
-
272
- return violations;
273
- }
274
-
275
- // Get line number for a match
276
- function getLineNumber(content, match) {
277
- const lines = content.substring(0, content.indexOf(match)).split('\n');
278
- return lines.length;
279
- }
280
-
281
- // Generate report
282
- function generateReport(allViolations, manifest) {
283
- const totalRestricted = allViolations.restrictedImports.length;
284
- const totalDuplicates =
285
- allViolations.duplicateComponents.length +
286
- allViolations.duplicateHooks.length +
287
- allViolations.duplicateUtils.length;
288
- const totalSuggestions = allViolations.suggestions.length;
289
- const totalRbacAuth =
290
- allViolations.customAuthCode.length +
291
- allViolations.duplicateConfig.length +
292
- allViolations.unprotectedPages.length +
293
- allViolations.directSupabaseAuth.length;
294
- const totalIssues = totalRestricted + totalDuplicates + totalSuggestions + totalRbacAuth;
295
-
296
- console.log(`\n${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}`);
297
- console.log(`${colors.bold}${colors.cyan} pace-core Compliance Report${colors.reset}`);
298
- console.log(`${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}\n`);
299
-
300
- // Restricted Imports
301
- if (totalRestricted > 0) {
302
- console.log(`${colors.red}${colors.bold}❌ Restricted Imports Found: ${totalRestricted}${colors.reset}\n`);
303
- allViolations.restrictedImports.forEach(({ module, reason, file, line }) => {
304
- console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}:${line}${colors.reset}`);
305
- console.log(` Import: ${colors.cyan}${module}${colors.reset}`);
306
- console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
307
- });
308
- } else {
309
- console.log(`${colors.green}✅ No restricted imports found${colors.reset}\n`);
310
- }
311
-
312
- // Duplicate Components
313
- if (allViolations.duplicateComponents.length > 0) {
314
- console.log(`${colors.red}${colors.bold}❌ Duplicate Components Found: ${allViolations.duplicateComponents.length}${colors.reset}\n`);
315
- allViolations.duplicateComponents.forEach(({ component, file }) => {
316
- console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
317
- console.log(` Component '${colors.cyan}${component}${colors.reset}' conflicts with pace-core component`);
318
- console.log(` ${colors.yellow}Suggestion:${colors.reset} Use '${component}' from '@jmruthers/pace-core' instead\n`);
319
- });
320
- }
321
-
322
- // Duplicate Hooks
323
- if (allViolations.duplicateHooks.length > 0) {
324
- console.log(`${colors.red}${colors.bold}❌ Duplicate Hooks Found: ${allViolations.duplicateHooks.length}${colors.reset}\n`);
325
- allViolations.duplicateHooks.forEach(({ hook, file }) => {
326
- console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
327
- console.log(` Hook '${colors.cyan}${hook}${colors.reset}' conflicts with pace-core hook`);
328
- console.log(` ${colors.yellow}Suggestion:${colors.reset} Use '${hook}' from '@jmruthers/pace-core' instead\n`);
329
- });
330
- }
331
-
332
- // Duplicate Utils
333
- if (allViolations.duplicateUtils.length > 0) {
334
- console.log(`${colors.red}${colors.bold}❌ Duplicate Utils Found: ${allViolations.duplicateUtils.length}${colors.reset}\n`);
335
- allViolations.duplicateUtils.forEach(({ util, file }) => {
336
- console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
337
- console.log(` Util '${colors.cyan}${util}${colors.reset}' conflicts with pace-core util`);
338
- console.log(` ${colors.yellow}Suggestion:${colors.reset} Use '${util}' from '@jmruthers/pace-core' instead\n`);
339
- });
340
- }
341
-
342
- // Suggestions
343
- if (totalSuggestions > 0) {
344
- console.log(`${colors.yellow}${colors.bold}💡 Suggestions: ${totalSuggestions}${colors.reset}\n`);
345
- const grouped = {};
346
- allViolations.suggestions.forEach(s => {
347
- if (!grouped[s.file]) grouped[s.file] = [];
348
- grouped[s.file].push(s);
349
- });
350
- Object.entries(grouped).forEach(([file, suggestions]) => {
351
- console.log(` ${colors.yellow}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
352
- suggestions.forEach(s => {
353
- console.log(` ${s.suggestion}\n`);
354
- });
355
- });
356
- }
357
-
358
- // RBAC/Auth Compliance Section
359
- if (totalRbacAuth > 0) {
360
- console.log(`\n${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}`);
361
- console.log(`${colors.bold}${colors.cyan} RBAC/Auth Compliance${colors.reset}`);
362
- console.log(`${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}\n`);
363
-
364
- // Custom Auth/RBAC Code
365
- if (allViolations.customAuthCode.length > 0) {
366
- console.log(`${colors.red}${colors.bold}❌ Custom Auth/RBAC Code Found: ${allViolations.customAuthCode.length}${colors.reset}\n`);
367
- allViolations.customAuthCode.forEach(({ name, type, file, line, reason }) => {
368
- console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}:${line}${colors.reset}`);
369
- console.log(` ${type}: ${colors.cyan}${name}${colors.reset}`);
370
- console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
371
- });
372
- }
373
-
374
- // Duplicate Configurations
375
- if (allViolations.duplicateConfig.length > 0) {
376
- console.log(`${colors.red}${colors.bold}❌ Duplicate Configurations Found: ${allViolations.duplicateConfig.length}${colors.reset}\n`);
377
- allViolations.duplicateConfig.forEach(({ type, file, count, reason }) => {
378
- console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
379
- console.log(` Type: ${colors.cyan}${type}${colors.reset}${count ? ` (${count} instances)` : ''}`);
380
- console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
381
- });
382
- }
383
-
384
- // Unprotected Pages
385
- if (allViolations.unprotectedPages.length > 0) {
386
- console.log(`${colors.red}${colors.bold}❌ Unprotected Pages Found: ${allViolations.unprotectedPages.length}${colors.reset}\n`);
387
- allViolations.unprotectedPages.forEach(({ file, reason }) => {
388
- console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}${colors.reset}`);
389
- console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
390
- });
391
- }
392
-
393
- // Direct Supabase Auth Usage
394
- if (allViolations.directSupabaseAuth.length > 0) {
395
- console.log(`${colors.red}${colors.bold}❌ Direct Supabase Auth Usage Found: ${allViolations.directSupabaseAuth.length}${colors.reset}\n`);
396
- allViolations.directSupabaseAuth.forEach(({ file, line, reason }) => {
397
- console.log(` ${colors.red}•${colors.reset} ${colors.yellow}${file}:${line}${colors.reset}`);
398
- console.log(` ${colors.yellow}Reason:${colors.reset} ${reason}\n`);
399
- });
400
- }
401
- } else {
402
- console.log(`\n${colors.green}✅ RBAC/Auth compliance: All checks passed${colors.reset}\n`);
403
- }
404
-
405
- // Summary
406
- console.log(`${colors.bold}${colors.cyan}═══════════════════════════════════════════════════════════${colors.reset}`);
407
- console.log(`${colors.bold}Summary:${colors.reset}`);
408
- console.log(` Total Issues: ${totalIssues > 0 ? colors.red : colors.green}${totalIssues}${colors.reset}`);
409
- console.log(` - Restricted Imports: ${totalRestricted > 0 ? colors.red : colors.green}${totalRestricted}${colors.reset}`);
410
- console.log(` - Duplicate Components/Hooks/Utils: ${totalDuplicates > 0 ? colors.red : colors.green}${totalDuplicates}${colors.reset}`);
411
- console.log(` - Suggestions: ${colors.yellow}${totalSuggestions}${colors.reset}`);
412
- console.log(` - RBAC/Auth Issues: ${totalRbacAuth > 0 ? colors.red : colors.green}${totalRbacAuth}${colors.reset}`);
413
-
414
- if (totalIssues === 0) {
415
- console.log(`\n${colors.green}${colors.bold}✅ Excellent! Your codebase is fully compliant with pace-core standards.${colors.reset}\n`);
416
- return 0;
417
- } else {
418
- console.log(`\n${colors.yellow}${colors.bold}⚠️ Please review the issues above and migrate to pace-core components/hooks/utils.${colors.reset}\n`);
419
- return 1;
420
- }
421
- }
422
-
423
- // Recursively find source files
424
- function findSourceFiles(dir, fileList = []) {
425
- const ignoreDirs = ['node_modules', 'dist', 'build', '.next', 'coverage', '__tests__', '__mocks__'];
426
- const ignoreFiles = /\.(test|spec)\.(ts|tsx|js|jsx)$/;
427
- const sourceExtensions = /\.(ts|tsx|js|jsx)$/;
428
-
429
- try {
430
- const items = fs.readdirSync(dir);
431
-
432
- items.forEach(item => {
433
- const fullPath = path.join(dir, item);
434
- const stat = fs.statSync(fullPath);
435
-
436
- if (stat.isDirectory()) {
437
- if (!ignoreDirs.includes(item) && !item.startsWith('.')) {
438
- findSourceFiles(fullPath, fileList);
439
- }
440
- } else if (stat.isFile()) {
441
- if (sourceExtensions.test(item) && !ignoreFiles.test(item)) {
442
- fileList.push(fullPath);
443
- }
444
- }
445
- });
446
- } catch (error) {
447
- // Skip directories we can't read
448
- }
449
-
450
- return fileList;
451
- }
452
-
453
- // Main function
454
- function main() {
455
- const manifest = loadManifest();
456
- const projectRoot = findProjectRoot();
457
-
458
- console.log(`${colors.cyan}Scanning project at: ${projectRoot}${colors.reset}`);
459
-
460
- // Find all TypeScript/JavaScript files (excluding node_modules, dist, etc.)
461
- const files = findSourceFiles(projectRoot);
462
-
463
- console.log(`Found ${files.length} files to scan...\n`);
464
-
465
- // Scan all files
466
- const allViolations = {
467
- restrictedImports: [],
468
- duplicateComponents: [],
469
- duplicateHooks: [],
470
- duplicateUtils: [],
471
- suggestions: [],
472
- customAuthCode: [],
473
- duplicateConfig: [],
474
- unprotectedPages: [],
475
- directSupabaseAuth: []
476
- };
477
-
478
- files.forEach(file => {
479
- try {
480
- const violations = scanFile(file, manifest);
481
- allViolations.restrictedImports.push(...violations.restrictedImports);
482
- allViolations.duplicateComponents.push(...violations.duplicateComponents);
483
- allViolations.duplicateHooks.push(...violations.duplicateHooks);
484
- allViolations.duplicateUtils.push(...violations.duplicateUtils);
485
- allViolations.suggestions.push(...violations.suggestions);
486
- allViolations.customAuthCode.push(...violations.customAuthCode);
487
- allViolations.duplicateConfig.push(...violations.duplicateConfig);
488
- allViolations.unprotectedPages.push(...violations.unprotectedPages);
489
- allViolations.directSupabaseAuth.push(...violations.directSupabaseAuth);
490
- } catch (error) {
491
- console.error(`${colors.red}Error scanning ${file}: ${error.message}${colors.reset}`);
492
- }
493
- });
494
-
495
- // Generate and display report
496
- const exitCode = generateReport(allViolations, manifest);
497
- process.exit(exitCode);
498
- }
499
-
500
- // Run if called directly
501
- if (require.main === module) {
502
- try {
503
- main();
504
- } catch (error) {
505
- console.error(`${colors.red}Error: ${error.message}${colors.reset}`);
506
- console.error(error.stack);
507
- process.exit(1);
508
- }
509
- }
510
-
511
- module.exports = { main, scanFile, generateReport };
512
-
@@ -1,126 +0,0 @@
1
- /**
2
- * @file useSuperAdminBypass
3
- * @package @jmruthers/pace-core
4
- *
5
- * Detects whether the current user is a super admin, keeps the
6
- * server session override flag in sync, and exposes a boolean
7
- * that downstream hooks can use to bypass organisation scoping.
8
- */
9
-
10
- import { useEffect, useMemo, useRef, useState } from 'react';
11
- import { useUnifiedAuth, type UnifiedAuthContextType } from '../../providers/services/UnifiedAuthProvider';
12
- import { isSuperAdmin as fetchIsSuperAdmin } from '../api';
13
- import { setSuperAdminOverrideFlag } from '../../utils/context/superAdminOverride';
14
- import { createLogger } from '../../utils/core/logger';
15
-
16
- const log = createLogger('useSuperAdminBypass');
17
-
18
- export interface SuperAdminBypassState {
19
- /** True when the user has been verified as a super admin */
20
- isSuperAdmin: boolean;
21
- /** True while the hook is checking the server */
22
- isLoading: boolean;
23
- /** Error returned by the verification request, if any */
24
- error: Error | null;
25
- }
26
-
27
- function useSafeUnifiedAuth(): UnifiedAuthContextType | null {
28
- try {
29
- return useUnifiedAuth();
30
- } catch (error) {
31
- log.debug('useSuperAdminBypass', 'UnifiedAuthProvider not available, falling back to defaults', {
32
- error: error instanceof Error ? error.message : error
33
- });
34
- return null;
35
- }
36
- }
37
-
38
- export function useSuperAdminBypass(): SuperAdminBypassState {
39
- const authContext = useSafeUnifiedAuth();
40
- const user = authContext?.user ?? null;
41
- const supabase = authContext?.supabase ?? null;
42
- const metadataHint =
43
- Boolean(user?.app_metadata?.is_super_admin) ||
44
- Boolean(user?.user_metadata?.is_super_admin);
45
-
46
- const [isSuperAdminState, setIsSuperAdminState] = useState<boolean>(metadataHint);
47
- const [hasVerified, setHasVerified] = useState<boolean>(!user?.id);
48
- const [isLoading, setIsLoading] = useState<boolean>(!!user?.id);
49
- const [error, setError] = useState<Error | null>(null);
50
- const lastOverrideValueRef = useRef<boolean | null>(null);
51
-
52
- // Verify against the RBAC engine whenever the user changes
53
- useEffect(() => {
54
- if (!user?.id) {
55
- setIsSuperAdminState(false);
56
- setHasVerified(true);
57
- setIsLoading(false);
58
- setError(null);
59
- return;
60
- }
61
-
62
- let cancelled = false;
63
- setIsLoading(true);
64
- setError(null);
65
-
66
- fetchIsSuperAdmin(user.id)
67
- .then((result) => {
68
- if (cancelled) {
69
- return;
70
- }
71
- setIsSuperAdminState(result);
72
- setHasVerified(true);
73
- })
74
- .catch((err) => {
75
- if (cancelled) {
76
- return;
77
- }
78
- const normalisedError =
79
- err instanceof Error ? err : new Error('Failed to resolve super admin status');
80
- setError(normalisedError);
81
- setIsSuperAdminState(false);
82
- setHasVerified(false);
83
- log.error('Unable to verify super admin status', normalisedError);
84
- })
85
- .finally(() => {
86
- if (!cancelled) {
87
- setIsLoading(false);
88
- }
89
- });
90
-
91
- return () => {
92
- cancelled = true;
93
- };
94
- }, [user?.id]);
95
-
96
- const shouldBypass = hasVerified && isSuperAdminState;
97
-
98
- // Keep the database session flag in sync for auditing/RLS helpers
99
- useEffect(() => {
100
- if (!supabase) {
101
- return;
102
- }
103
- if (lastOverrideValueRef.current === shouldBypass) {
104
- return;
105
- }
106
- lastOverrideValueRef.current = shouldBypass;
107
-
108
- setSuperAdminOverrideFlag({
109
- supabase,
110
- enabled: shouldBypass,
111
- reason: 'pace-core-super-admin-bypass'
112
- }).catch(() => {
113
- // Errors are logged inside the helper
114
- });
115
- }, [supabase, shouldBypass]);
116
-
117
- return useMemo(
118
- () => ({
119
- isSuperAdmin: shouldBypass,
120
- isLoading,
121
- error
122
- }),
123
- [shouldBypass, isLoading, error]
124
- );
125
- }
126
-
@@ -1,58 +0,0 @@
1
- /**
2
- * @file Super Admin Override Utility
3
- * @package @jmruthers/pace-core
4
- * @module Utils/SuperAdminOverride
5
- *
6
- * Provides helpers for toggling the database session flag that
7
- * signals a super admin override. This ensures SECURITY DEFINER
8
- * functions and RLS policies can audit elevated operations.
9
- */
10
-
11
- import type { SupabaseClient } from '@supabase/supabase-js';
12
- import { createLogger } from '../core/logger';
13
-
14
- const log = createLogger('superAdminOverride');
15
-
16
- interface SuperAdminOverrideParams {
17
- supabase: SupabaseClient | null | undefined;
18
- enabled: boolean;
19
- reason?: string;
20
- }
21
-
22
- /**
23
- * Toggle the super admin override flag in the current Supabase session.
24
- * Also records the action server-side for audit purposes.
25
- */
26
- export async function setSuperAdminOverrideFlag({
27
- supabase,
28
- enabled,
29
- reason = 'client_request'
30
- }: SuperAdminOverrideParams): Promise<void> {
31
- if (!supabase) {
32
- return;
33
- }
34
-
35
- try {
36
- const { error } = await supabase.rpc('set_super_admin_override', {
37
- p_enabled: enabled,
38
- p_reason: reason
39
- });
40
-
41
- if (error) {
42
- log.error('Failed to toggle super admin override', {
43
- enabled,
44
- reason,
45
- error: error.message
46
- });
47
- } else {
48
- log.debug('Super admin override flag updated', { enabled, reason });
49
- }
50
- } catch (rpcError) {
51
- log.error('Unexpected error toggling super admin override', {
52
- enabled,
53
- reason,
54
- error: rpcError instanceof Error ? rpcError.message : rpcError
55
- });
56
- }
57
- }
58
-