@jmruthers/pace-core 0.5.136 → 0.5.137

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/dist/{DataTable-CYOHOX3O.js → DataTable-6M4L6BI2.js} +10 -9
  2. package/dist/{EventLogo-801uofbR.d.ts → EventLogo-rFL_kRjk.d.ts} +73 -1
  3. package/dist/{UnifiedAuthProvider-5E5TUNMS.js → UnifiedAuthProvider-XIQQ7LVU.js} +4 -5
  4. package/dist/{chunk-YLKIDTUK.js → chunk-22WKWKRX.js} +4 -4
  5. package/dist/{chunk-TVYPTYOY.js → chunk-4C7EXCAR.js} +60 -24
  6. package/dist/chunk-4C7EXCAR.js.map +1 -0
  7. package/dist/{chunk-2TWNJ46Y.js → chunk-6LAAY47Q.js} +2 -2
  8. package/dist/{chunk-444EZN6N.js → chunk-7QCC6MCP.js} +88 -1
  9. package/dist/chunk-7QCC6MCP.js.map +1 -0
  10. package/dist/{chunk-FHWWBIHA.js → chunk-BCIBECNB.js} +5 -5
  11. package/dist/chunk-BJPBT3CU.js +21 -0
  12. package/dist/chunk-BJPBT3CU.js.map +1 -0
  13. package/dist/{chunk-L6PGMCMD.js → chunk-BLCXZEYF.js} +3 -3
  14. package/dist/{chunk-HJGGOMQ6.js → chunk-HAWZXGR2.js} +147 -103
  15. package/dist/chunk-HAWZXGR2.js.map +1 -0
  16. package/dist/{chunk-XARJS7CD.js → chunk-INQLMHPF.js} +2 -2
  17. package/dist/chunk-JISYG63F.js +70 -0
  18. package/dist/chunk-JISYG63F.js.map +1 -0
  19. package/dist/{chunk-NOHEVYVX.js → chunk-KYRHUBIU.js} +417 -319
  20. package/dist/chunk-KYRHUBIU.js.map +1 -0
  21. package/dist/{chunk-SL2YQDR6.js → chunk-MA6EPSGZ.js} +2 -2
  22. package/dist/{chunk-5DPZ5EAT.js → chunk-OWAG3GSU.js} +1 -3
  23. package/dist/{chunk-LTV3XIJJ.js → chunk-T6JN6LH6.js} +4 -4
  24. package/dist/{chunk-4MT5BGGL.js → chunk-YCWDTTUK.js} +4 -6
  25. package/dist/{chunk-4MT5BGGL.js.map → chunk-YCWDTTUK.js.map} +1 -1
  26. package/dist/components.d.ts +1 -1
  27. package/dist/components.js +12 -11
  28. package/dist/components.js.map +1 -1
  29. package/dist/hooks.js +8 -9
  30. package/dist/hooks.js.map +1 -1
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +15 -14
  33. package/dist/index.js.map +1 -1
  34. package/dist/providers.js +3 -4
  35. package/dist/rbac/index.js +8 -9
  36. package/dist/schema-DTDZQe2u.d.ts +28 -0
  37. package/dist/types.d.ts +152 -3
  38. package/dist/types.js +51 -16
  39. package/dist/types.js.map +1 -1
  40. package/dist/utils.d.ts +89 -4
  41. package/dist/utils.js +214 -96
  42. package/dist/utils.js.map +1 -1
  43. package/dist/validation.d.ts +1 -343
  44. package/dist/validation.js +3 -100
  45. package/docs/api/classes/ColumnFactory.md +1 -1
  46. package/docs/api/classes/ErrorBoundary.md +1 -1
  47. package/docs/api/classes/InvalidScopeError.md +1 -1
  48. package/docs/api/classes/MissingUserContextError.md +1 -1
  49. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  50. package/docs/api/classes/PermissionDeniedError.md +1 -1
  51. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  52. package/docs/api/classes/RBACAuditManager.md +1 -1
  53. package/docs/api/classes/RBACCache.md +1 -1
  54. package/docs/api/classes/RBACEngine.md +1 -1
  55. package/docs/api/classes/RBACError.md +1 -1
  56. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  57. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  58. package/docs/api/classes/StorageUtils.md +1 -1
  59. package/docs/api/enums/FileCategory.md +1 -1
  60. package/docs/api/interfaces/AggregateConfig.md +1 -1
  61. package/docs/api/interfaces/BadgeProps.md +27 -0
  62. package/docs/api/interfaces/ButtonProps.md +1 -1
  63. package/docs/api/interfaces/CardProps.md +1 -1
  64. package/docs/api/interfaces/ColorPalette.md +1 -1
  65. package/docs/api/interfaces/ColorShade.md +1 -1
  66. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  67. package/docs/api/interfaces/DataRecord.md +1 -1
  68. package/docs/api/interfaces/DataTableAction.md +1 -1
  69. package/docs/api/interfaces/DataTableColumn.md +1 -1
  70. package/docs/api/interfaces/DataTableProps.md +1 -1
  71. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  72. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  73. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  74. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  75. package/docs/api/interfaces/EventLogoProps.md +1 -1
  76. package/docs/api/interfaces/ExportColumn.md +1 -1
  77. package/docs/api/interfaces/ExportOptions.md +1 -1
  78. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  79. package/docs/api/interfaces/FileMetadata.md +1 -1
  80. package/docs/api/interfaces/FileReference.md +1 -1
  81. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  82. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  83. package/docs/api/interfaces/FileUploadProps.md +1 -1
  84. package/docs/api/interfaces/FooterProps.md +1 -1
  85. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  86. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  87. package/docs/api/interfaces/InputProps.md +1 -1
  88. package/docs/api/interfaces/LabelProps.md +1 -1
  89. package/docs/api/interfaces/LoginFormProps.md +1 -1
  90. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  91. package/docs/api/interfaces/NavigationContextType.md +1 -1
  92. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  93. package/docs/api/interfaces/NavigationItem.md +1 -1
  94. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  95. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  96. package/docs/api/interfaces/Organisation.md +1 -1
  97. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  98. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  99. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  100. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  101. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  102. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  103. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  104. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  105. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  106. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  107. package/docs/api/interfaces/PaletteData.md +1 -1
  108. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  109. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  110. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  111. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  112. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  113. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  114. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  115. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  116. package/docs/api/interfaces/RBACConfig.md +1 -1
  117. package/docs/api/interfaces/RBACLogger.md +1 -1
  118. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  119. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  120. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  121. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  122. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  123. package/docs/api/interfaces/RouteConfig.md +1 -1
  124. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  125. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  126. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  127. package/docs/api/interfaces/StorageConfig.md +1 -1
  128. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  129. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  130. package/docs/api/interfaces/StorageListOptions.md +1 -1
  131. package/docs/api/interfaces/StorageListResult.md +1 -1
  132. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  133. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  134. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  135. package/docs/api/interfaces/StyleImport.md +1 -1
  136. package/docs/api/interfaces/SwitchProps.md +1 -1
  137. package/docs/api/interfaces/ToastActionElement.md +1 -1
  138. package/docs/api/interfaces/ToastProps.md +1 -1
  139. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  140. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  141. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  142. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  143. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  144. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  145. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  146. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  147. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  148. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  149. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  150. package/docs/api/interfaces/UserEventAccess.md +1 -1
  151. package/docs/api/interfaces/UserMenuProps.md +1 -1
  152. package/docs/api/interfaces/UserProfile.md +1 -1
  153. package/docs/api/modules.md +79 -10
  154. package/docs/architecture/README.md +0 -1
  155. package/docs/styles/README.md +0 -2
  156. package/examples/RBAC/CompleteRBACExample.tsx +324 -0
  157. package/examples/RBAC/EventBasedApp.tsx +239 -0
  158. package/examples/RBAC/PermissionExample.tsx +151 -0
  159. package/examples/RBAC/index.ts +13 -0
  160. package/examples/public-pages/CorrectPublicPageImplementation.tsx +301 -0
  161. package/examples/public-pages/PublicEventPage.tsx +274 -0
  162. package/examples/public-pages/PublicPageApp.tsx +308 -0
  163. package/examples/public-pages/PublicPageUsageExample.tsx +216 -0
  164. package/examples/public-pages/index.ts +14 -0
  165. package/package.json +1 -10
  166. package/src/__tests__/TEST_STANDARD.md +92 -0
  167. package/src/components/Badge/Badge.test.tsx +314 -0
  168. package/src/components/Badge/Badge.tsx +304 -0
  169. package/src/components/Badge/index.ts +3 -0
  170. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +217 -0
  171. package/src/components/DataTable/__tests__/styles.test.ts +1 -1
  172. package/src/components/DataTable/components/ColumnFilter.tsx +8 -4
  173. package/src/components/DataTable/components/DataTableBody.tsx +461 -0
  174. package/src/components/DataTable/components/DraggableColumnHeader.tsx +144 -0
  175. package/src/components/DataTable/components/FilterRow.tsx +9 -3
  176. package/src/components/DataTable/components/PaginationControls.tsx +1 -0
  177. package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
  178. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +14 -68
  179. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +62 -0
  180. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +43 -0
  181. package/src/components/DataTable/core/ActionManager.ts +235 -0
  182. package/src/components/DataTable/core/ColumnManager.ts +205 -0
  183. package/src/components/DataTable/core/DataManager.ts +188 -0
  184. package/src/components/DataTable/core/DataTableContext.tsx +181 -0
  185. package/src/components/DataTable/core/LocalDataAdapter.ts +273 -0
  186. package/src/components/DataTable/core/PluginRegistry.ts +229 -0
  187. package/src/components/DataTable/core/StateManager.ts +311 -0
  188. package/src/components/DataTable/core/interfaces.ts +338 -0
  189. package/src/components/DataTable/styles.ts +27 -6
  190. package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +94 -0
  191. package/src/components/DataTable/utils/columnUtils.ts +40 -0
  192. package/src/components/DataTable/utils/debugTools.ts +609 -0
  193. package/src/components/DataTable/utils/index.ts +1 -0
  194. package/src/components/Dialog/README.md +804 -0
  195. package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +611 -0
  196. package/src/components/Dialog/utils/safeHtml.ts +185 -0
  197. package/src/components/Footer/Footer.test.tsx +1 -1
  198. package/src/components/Form/Form.test.tsx +1 -1
  199. package/src/components/Form/FormErrorSummary.tsx +113 -0
  200. package/src/components/Form/FormFieldset.tsx +127 -0
  201. package/src/components/Form/FormLiveRegion.tsx +198 -0
  202. package/src/components/LoginForm/LoginForm.test.tsx +1 -1
  203. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +76 -10
  204. package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
  205. package/src/components/PasswordReset/PasswordResetForm.test.tsx +597 -0
  206. package/src/components/PasswordReset/PasswordResetForm.tsx +201 -0
  207. package/src/components/PublicLayout/PublicPageDebugger.tsx +104 -0
  208. package/src/components/PublicLayout/PublicPageDiagnostic.tsx +162 -0
  209. package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
  210. package/src/components/Select/Select.test.tsx +1 -1
  211. package/src/components/Select/Select.tsx +20 -8
  212. package/src/components/Table/__tests__/Table.test.tsx +1 -1
  213. package/src/components/index.ts +3 -0
  214. package/src/hooks/__tests__/useFileUrl.unit.test.ts +83 -85
  215. package/src/index.ts +4 -0
  216. package/src/styles/core.css +3 -0
  217. package/src/utils/appConfig.ts +47 -0
  218. package/src/utils/appIdResolver.test.ts +499 -0
  219. package/src/utils/appIdResolver.ts +130 -0
  220. package/src/utils/appNameResolver.simple.test.ts +212 -0
  221. package/src/utils/appNameResolver.test.ts +121 -0
  222. package/src/utils/appNameResolver.ts +191 -0
  223. package/src/utils/audit.ts +127 -0
  224. package/src/utils/auth-utils.ts +96 -0
  225. package/src/utils/bundleAnalysis.ts +129 -0
  226. package/src/utils/cn.ts +7 -0
  227. package/src/utils/debugLogger.ts +67 -0
  228. package/src/utils/deviceFingerprint.ts +215 -0
  229. package/src/utils/dynamicUtils.ts +105 -0
  230. package/src/utils/file-reference.test.ts +788 -0
  231. package/src/utils/file-reference.ts +519 -0
  232. package/src/utils/formatDate.test.ts +237 -0
  233. package/src/utils/formatting.ts +133 -0
  234. package/src/utils/index.ts +7 -0
  235. package/src/utils/lazyLoad.tsx +44 -0
  236. package/src/utils/logger.ts +179 -0
  237. package/src/utils/organisationContext.test.ts +322 -0
  238. package/src/utils/organisationContext.ts +153 -0
  239. package/src/utils/performanceBenchmark.ts +64 -0
  240. package/src/utils/performanceBudgets.ts +110 -0
  241. package/src/utils/permissionTypes.ts +37 -0
  242. package/src/utils/permissionUtils.test.ts +393 -0
  243. package/src/utils/permissionUtils.ts +34 -0
  244. package/src/utils/sanitization.ts +264 -0
  245. package/src/utils/schemaUtils.ts +37 -0
  246. package/src/utils/secureDataAccess.test.ts +711 -0
  247. package/src/utils/secureDataAccess.ts +377 -0
  248. package/src/utils/secureErrors.ts +79 -0
  249. package/src/utils/secureStorage.ts +244 -0
  250. package/src/utils/security.ts +156 -0
  251. package/src/utils/securityMonitor.ts +45 -0
  252. package/src/utils/sessionTracking.ts +126 -0
  253. package/src/utils/validation.ts +111 -0
  254. package/src/utils/validationUtils.ts +120 -0
  255. package/src/validation/index.ts +2 -2
  256. package/dist/chunk-444EZN6N.js.map +0 -1
  257. package/dist/chunk-APIBCTL2.js +0 -670
  258. package/dist/chunk-APIBCTL2.js.map +0 -1
  259. package/dist/chunk-HJGGOMQ6.js.map +0 -1
  260. package/dist/chunk-K2WWTH7O.js +0 -94
  261. package/dist/chunk-K2WWTH7O.js.map +0 -1
  262. package/dist/chunk-LMC26NLJ.js +0 -84
  263. package/dist/chunk-LMC26NLJ.js.map +0 -1
  264. package/dist/chunk-NOHEVYVX.js.map +0 -1
  265. package/dist/chunk-TVYPTYOY.js.map +0 -1
  266. package/dist/validation-8npbysjg.d.ts +0 -177
  267. /package/dist/{DataTable-CYOHOX3O.js.map → DataTable-6M4L6BI2.js.map} +0 -0
  268. /package/dist/{UnifiedAuthProvider-5E5TUNMS.js.map → UnifiedAuthProvider-XIQQ7LVU.js.map} +0 -0
  269. /package/dist/{chunk-YLKIDTUK.js.map → chunk-22WKWKRX.js.map} +0 -0
  270. /package/dist/{chunk-2TWNJ46Y.js.map → chunk-6LAAY47Q.js.map} +0 -0
  271. /package/dist/{chunk-FHWWBIHA.js.map → chunk-BCIBECNB.js.map} +0 -0
  272. /package/dist/{chunk-L6PGMCMD.js.map → chunk-BLCXZEYF.js.map} +0 -0
  273. /package/dist/{chunk-XARJS7CD.js.map → chunk-INQLMHPF.js.map} +0 -0
  274. /package/dist/{chunk-SL2YQDR6.js.map → chunk-MA6EPSGZ.js.map} +0 -0
  275. /package/dist/{chunk-5DPZ5EAT.js.map → chunk-OWAG3GSU.js.map} +0 -0
  276. /package/dist/{chunk-LTV3XIJJ.js.map → chunk-T6JN6LH6.js.map} +0 -0
  277. /package/examples/{components → components 2}/DataTable/HierarchicalActionsExample.tsx +0 -0
  278. /package/examples/{components → components 2}/DataTable/HierarchicalExample.tsx +0 -0
  279. /package/examples/{components → components 2}/DataTable/InitialPageSizeExample.tsx +0 -0
  280. /package/examples/{components → components 2}/DataTable/PerformanceExample.tsx +0 -0
  281. /package/examples/{components → components 2}/DataTable/index.ts +0 -0
  282. /package/examples/{components → components 2}/Dialog/BasicHtmlTest.tsx +0 -0
  283. /package/examples/{components → components 2}/Dialog/DebugHtmlExample.tsx +0 -0
  284. /package/examples/{components → components 2}/Dialog/HtmlDialogExample.tsx +0 -0
  285. /package/examples/{components → components 2}/Dialog/ScrollableDialogExample.tsx +0 -0
  286. /package/examples/{components → components 2}/Dialog/SimpleHtmlTest.tsx +0 -0
  287. /package/examples/{components → components 2}/Dialog/SmartDialogExample.tsx +0 -0
  288. /package/examples/{components → components 2}/Dialog/index.ts +0 -0
  289. /package/examples/{components → components 2}/index.ts +0 -0
@@ -0,0 +1,156 @@
1
+
2
+ import type { SupabaseClient } from '@supabase/supabase-js';
3
+
4
+ export interface SecurityEvent {
5
+ type: string;
6
+ timestamp: Date;
7
+ userId?: string;
8
+ details: Record<string, unknown>;
9
+ }
10
+
11
+ export function logSecurityEvent(event: SecurityEvent): void {
12
+ // In production, this should log to your security monitoring system
13
+ // For now, we'll log to console.warn for testing purposes
14
+ console.warn('[SECURITY EVENT]', {
15
+ ...event,
16
+ timestamp: event.timestamp.toISOString()
17
+ });
18
+ }
19
+
20
+ export function validateUserSession(userId: string, sessionToken?: string): Promise<boolean> {
21
+ // Mock implementation - replace with actual session validation
22
+ if (!userId || typeof userId !== 'string') {
23
+ logSecurityEvent({
24
+ type: 'invalid_session_validation',
25
+ timestamp: new Date(),
26
+ details: { reason: 'Invalid userId provided' }
27
+ });
28
+ return Promise.resolve(false);
29
+ }
30
+
31
+ if (sessionToken && sessionToken.length < 10) {
32
+ logSecurityEvent({
33
+ type: 'suspicious_session_token',
34
+ timestamp: new Date(),
35
+ userId,
36
+ details: { reason: 'Session token too short' }
37
+ });
38
+ return Promise.resolve(false);
39
+ }
40
+
41
+ return Promise.resolve(true);
42
+ }
43
+
44
+ export function createSecureSession(
45
+ _supabaseClient: SupabaseClient,
46
+ sessionData: { userId: string; deviceFingerprint?: string }
47
+ ): Promise<string> {
48
+ // Mock implementation - in production, create actual secure session
49
+ const sessionId = `sess_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
50
+
51
+ logSecurityEvent({
52
+ type: 'session_created',
53
+ timestamp: new Date(),
54
+ userId: sessionData.userId,
55
+ details: {
56
+ sessionId,
57
+ hasDeviceFingerprint: !!sessionData.deviceFingerprint
58
+ }
59
+ });
60
+
61
+ return Promise.resolve(sessionId);
62
+ }
63
+
64
+ export function invalidateSession(
65
+ _supabaseClient: SupabaseClient,
66
+ sessionId: string
67
+ ): Promise<void> {
68
+ // Mock implementation - in production, invalidate actual session
69
+ logSecurityEvent({
70
+ type: 'session_invalidated',
71
+ timestamp: new Date(),
72
+ details: { sessionId }
73
+ });
74
+
75
+ return Promise.resolve();
76
+ }
77
+
78
+ export function getSecurityHeaders(): Record<string, string> {
79
+ return {
80
+ 'X-Content-Type-Options': 'nosniff',
81
+ 'X-Frame-Options': 'DENY',
82
+ 'X-XSS-Protection': '1; mode=block',
83
+ 'Referrer-Policy': 'strict-origin-when-cross-origin',
84
+ 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains'
85
+ };
86
+ }
87
+
88
+ export function validateSecurityHeaders(headers: Record<string, string>): boolean {
89
+ const requiredHeaders = ['X-Content-Type-Options', 'X-Frame-Options'];
90
+ const missingHeaders = requiredHeaders.filter(header => !headers[header]);
91
+
92
+ if (missingHeaders.length > 0) {
93
+ logSecurityEvent({
94
+ type: 'missing_security_headers',
95
+ timestamp: new Date(),
96
+ details: { missingHeaders }
97
+ });
98
+ return false;
99
+ }
100
+
101
+ return true;
102
+ }
103
+
104
+ export function generateDeviceFingerprint(): string {
105
+ // Basic device fingerprinting - in production, use more sophisticated methods
106
+ const canvas = document.createElement('canvas');
107
+ const ctx = canvas.getContext('2d');
108
+ if (ctx) {
109
+ ctx.textBaseline = 'top';
110
+ ctx.font = '14px Arial';
111
+ ctx.fillText('Device fingerprint', 2, 2);
112
+ }
113
+
114
+ const fingerprint = [
115
+ navigator.userAgent,
116
+ navigator.language,
117
+ screen.width + 'x' + screen.height,
118
+ new Date().getTimezoneOffset(),
119
+ canvas.toDataURL()
120
+ ].join('|');
121
+
122
+ // Simple hash function for demonstration
123
+ let hash = 0;
124
+ for (let i = 0; i < fingerprint.length; i++) {
125
+ const char = fingerprint.charCodeAt(i);
126
+ hash = ((hash << 5) - hash) + char;
127
+ hash = hash & hash; // Convert to 32-bit integer
128
+ }
129
+
130
+ return Math.abs(hash).toString(16);
131
+ }
132
+
133
+ export function validateDeviceFingerprint(fingerprint: string, expectedFingerprint?: string): boolean {
134
+ if (!fingerprint || typeof fingerprint !== 'string') {
135
+ logSecurityEvent({
136
+ type: 'invalid_device_fingerprint',
137
+ timestamp: new Date(),
138
+ details: { reason: 'Invalid fingerprint format' }
139
+ });
140
+ return false;
141
+ }
142
+
143
+ if (expectedFingerprint && fingerprint !== expectedFingerprint) {
144
+ logSecurityEvent({
145
+ type: 'device_fingerprint_mismatch',
146
+ timestamp: new Date(),
147
+ details: {
148
+ provided: fingerprint,
149
+ expected: expectedFingerprint
150
+ }
151
+ });
152
+ return false;
153
+ }
154
+
155
+ return true;
156
+ }
@@ -0,0 +1,45 @@
1
+
2
+ interface SecurityEvent {
3
+ id?: string;
4
+ action: string;
5
+ details: Record<string, any>;
6
+ timestamp?: number;
7
+ }
8
+
9
+ interface SecurityAlert {
10
+ id: string;
11
+ type: string;
12
+ message: string;
13
+ timestamp: Date;
14
+ }
15
+
16
+ class SecurityMonitor {
17
+ private events: SecurityEvent[] = [];
18
+
19
+ logEvent(event: SecurityEvent) {
20
+ const eventWithId = {
21
+ ...event,
22
+ id: Math.random().toString(36).substr(2, 9),
23
+ timestamp: Date.now()
24
+ };
25
+ this.events.push(eventWithId);
26
+ }
27
+
28
+ getEvents(): SecurityEvent[] {
29
+ return [...this.events];
30
+ }
31
+
32
+ clearEvents() {
33
+ this.events = [];
34
+ }
35
+
36
+ createAlert(alert: Omit<SecurityAlert, 'id' | 'timestamp'>): SecurityAlert {
37
+ return {
38
+ ...alert,
39
+ id: Math.random().toString(36).substr(2, 9),
40
+ timestamp: new Date()
41
+ };
42
+ }
43
+ }
44
+
45
+ export const securityMonitor = new SecurityMonitor();
@@ -0,0 +1,126 @@
1
+ import type { SupabaseClient } from '@supabase/supabase-js';
2
+
3
+ // Define the tracking parameters locally since old RBAC types are removed
4
+ interface TrackUserSessionParams {
5
+ p_session_type: 'event_switch' | 'session_expired';
6
+ p_event_id?: string;
7
+ p_app_id?: string;
8
+ ip_address?: string;
9
+ user_agent?: string;
10
+ }
11
+
12
+ /**
13
+ * Hook for manual session tracking (event switches and session expiration).
14
+ *
15
+ * Note: Login and logout tracking is automatically handled by UnifiedAuthProvider.
16
+ * You should only use this hook for tracking event switches or session expirations.
17
+ *
18
+ * @param supabaseClient - Supabase client instance
19
+ * @param appName - Optional application name for tracking
20
+ * @returns Object containing tracking functions for event switches and session expiration
21
+ */
22
+ export function useSessionTracking(supabaseClient: SupabaseClient, appName?: string) {
23
+ // Resolve app name to app_id
24
+ const resolveAppId = async (): Promise<string | undefined> => {
25
+ if (!appName) return undefined;
26
+
27
+ try {
28
+ const { data, error } = await supabaseClient
29
+ .from('rbac_apps')
30
+ .select('id')
31
+ .eq('name', appName)
32
+ .eq('is_active', true)
33
+ .single();
34
+
35
+ if (error || !data) {
36
+ console.warn('App not found or inactive:', appName);
37
+ return undefined;
38
+ }
39
+
40
+ return data.id;
41
+ } catch (error) {
42
+ console.error('Failed to resolve app ID:', error);
43
+ return undefined;
44
+ }
45
+ };
46
+ /**
47
+ * Track an event switch
48
+ * @param eventId - ID of the event being switched to
49
+ */
50
+ const trackEventSwitch = async (eventId: string) => {
51
+ try {
52
+ const { data: { user } } = await supabaseClient.auth.getUser();
53
+ if (!user) {
54
+ console.warn('No authenticated user found for session tracking');
55
+ return;
56
+ }
57
+
58
+ const appId = await resolveAppId();
59
+
60
+ const params: TrackUserSessionParams = {
61
+ p_session_type: 'event_switch',
62
+ p_event_id: eventId,
63
+ p_app_id: appId
64
+ };
65
+
66
+ const { error } = await supabaseClient.rpc('rbac_session_track', {
67
+ p_user_id: user?.id,
68
+ p_session_type: params.p_session_type,
69
+ p_event_id: params.p_event_id,
70
+ p_app_id: params.p_app_id,
71
+ p_ip_address: params.ip_address,
72
+ p_user_agent: params.user_agent
73
+ });
74
+
75
+ if (error) {
76
+ console.error('Failed to track event switch session:', error);
77
+ } else {
78
+ console.log('Event switch session tracked successfully');
79
+ }
80
+ } catch (error) {
81
+ console.error('Failed to track event switch:', error);
82
+ }
83
+ };
84
+
85
+ /**
86
+ * Track a session expiration
87
+ */
88
+ const trackSessionExpired = async () => {
89
+ try {
90
+ const { data: { user } } = await supabaseClient.auth.getUser();
91
+ if (!user) {
92
+ console.warn('No authenticated user found for session tracking');
93
+ return;
94
+ }
95
+
96
+ const appId = await resolveAppId();
97
+
98
+ const params: TrackUserSessionParams = {
99
+ p_session_type: 'session_expired',
100
+ p_app_id: appId
101
+ };
102
+
103
+ const { error } = await supabaseClient.rpc('rbac_session_track', {
104
+ p_user_id: user?.id,
105
+ p_session_type: params.p_session_type,
106
+ p_event_id: params.p_event_id,
107
+ p_app_id: params.p_app_id,
108
+ p_ip_address: params.ip_address,
109
+ p_user_agent: params.user_agent
110
+ });
111
+
112
+ if (error) {
113
+ console.error('Failed to track session expiration:', error);
114
+ } else {
115
+ console.log('Session expiration tracked successfully');
116
+ }
117
+ } catch (error) {
118
+ console.error('Failed to track session expiration:', error);
119
+ }
120
+ };
121
+
122
+ return {
123
+ trackEventSwitch,
124
+ trackSessionExpired
125
+ };
126
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * @file Internal utilities for validation module
3
+ * @internal This file contains implementation details that should not be used directly
4
+ */
5
+
6
+ /**
7
+ * Utility functions for validating data in the application
8
+ */
9
+
10
+ /**
11
+ * Check if a string is a valid email
12
+ */
13
+ export function isValidEmail(email: string): boolean {
14
+ const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
15
+ return emailPattern.test(email);
16
+ }
17
+
18
+ /**
19
+ * Check if a string is empty (either null, undefined, or just whitespace)
20
+ */
21
+ export function isEmpty(value: string | null | undefined): boolean {
22
+ return value === null || value === undefined || value.trim() === '';
23
+ }
24
+
25
+ /**
26
+ * Check if a password meets minimum requirements
27
+ */
28
+ export function isStrongPassword(password: string): boolean {
29
+ // Minimum 8 characters, at least one uppercase, one lowercase, one number
30
+ const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;
31
+ return passwordPattern.test(password);
32
+ }
33
+
34
+ /**
35
+ * Check if a URL is valid
36
+ */
37
+ export function isValidUrl(url: string): boolean {
38
+ try {
39
+ new URL(url);
40
+ return true;
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Check if a date string is valid
48
+ */
49
+ export function isValidDate(dateStr: string): boolean {
50
+ const date = new Date(dateStr);
51
+ return !isNaN(date.getTime());
52
+ }
53
+
54
+ /**
55
+ * Check if a value is within a range
56
+ */
57
+ export function isWithinRange(value: number, min: number, max: number): boolean {
58
+ return value >= min && value <= max;
59
+ }
60
+
61
+ /**
62
+ * Check if a value matches a specific pattern
63
+ */
64
+ export function matchesPattern(value: string, pattern: RegExp): boolean {
65
+ return pattern.test(value);
66
+ }
67
+
68
+ /**
69
+ * Utility function to deep merge objects for schema combination
70
+ * @internal
71
+ */
72
+ export function deepMerge<T extends Record<string, unknown>>(
73
+ target: T,
74
+ source: Record<string, unknown>
75
+ ): T {
76
+ const output = { ...target };
77
+
78
+ if (isObject(target) && isObject(source)) {
79
+ Object.keys(source).forEach(key => {
80
+ if (isObject(source[key])) {
81
+ if (!(key in target)) {
82
+ Object.assign(output, { [key]: source[key] });
83
+ } else {
84
+ // Use a type assertion to safely handle the indexing
85
+ const targetKey = key as keyof typeof target;
86
+ const targetValue = target[targetKey];
87
+
88
+ if (isObject(targetValue)) {
89
+ // Safe cast using type assertion
90
+ output[targetKey] = deepMerge(
91
+ targetValue as Record<string, unknown>,
92
+ source[key] as Record<string, unknown>
93
+ ) as unknown as T[keyof T];
94
+ }
95
+ }
96
+ } else {
97
+ Object.assign(output, { [key]: source[key] });
98
+ }
99
+ });
100
+ }
101
+
102
+ return output as T;
103
+ }
104
+
105
+ /**
106
+ * Type guard to check if a value is a plain object
107
+ * @internal
108
+ */
109
+ export function isObject(item: unknown): item is Record<string, unknown> {
110
+ return item !== null && typeof item === 'object' && !Array.isArray(item);
111
+ }
@@ -0,0 +1,120 @@
1
+
2
+ /**
3
+ * @file Validation utilities
4
+ *
5
+ * Shared validation utilities with enhanced security
6
+ */
7
+
8
+ import { z } from 'zod';
9
+ import { sanitizeUserInput, sanitizeFormData, type SanitizationOptions } from './sanitization';
10
+
11
+ /**
12
+ * Validates user input against a schema with automatic sanitization
13
+ */
14
+ export function validateUserInput<T>(
15
+ schema: z.ZodSchema<T>,
16
+ data: unknown,
17
+ sanitizationRules?: Record<string, SanitizationOptions>
18
+ ): { success: boolean; data?: T; error?: string } {
19
+ return sanitizeFormData(data, schema, sanitizationRules);
20
+ }
21
+
22
+ /**
23
+ * Sanitizes user input by removing potentially dangerous characters
24
+ * @deprecated Use sanitizeUserInput from lib/sanitization instead
25
+ */
26
+ export function sanitizeUserInput_deprecated(input: string): string {
27
+ // Log deprecation warning
28
+ console.warn('sanitizeUserInput is deprecated. Use sanitizeUserInput from lib/sanitization instead.');
29
+ return sanitizeUserInput(input);
30
+ }
31
+
32
+ /**
33
+ * Enhanced email validation with sanitization
34
+ */
35
+ export const emailSchema = z.string()
36
+ .transform(email => email.toLowerCase().trim())
37
+ .pipe(z.string().min(1, 'Email is required').email('Invalid email format').max(254, 'Email too long'));
38
+
39
+ /**
40
+ * Enhanced password validation
41
+ */
42
+ export const passwordSchema = z.string()
43
+ .min(8, 'Password must be at least 8 characters')
44
+ .max(128, 'Password too long')
45
+ .regex(/[A-Z]/, 'Password must contain at least one uppercase letter')
46
+ .regex(/[a-z]/, 'Password must contain at least one lowercase letter')
47
+ .regex(/[0-9]/, 'Password must contain at least one number')
48
+ .regex(/[^A-Za-z0-9]/, 'Password must contain at least one special character');
49
+
50
+ /**
51
+ * Username validation with sanitization
52
+ */
53
+ export const usernameSchema = z.string()
54
+ .transform(username => username.toLowerCase().trim())
55
+ .pipe(z.string().min(3, 'Username must be at least 3 characters').max(30, 'Username too long').regex(/^[a-zA-Z0-9_-]+$/, 'Username can only contain letters, numbers, hyphens, and underscores'));
56
+
57
+ /**
58
+ * Name validation with sanitization
59
+ */
60
+ export const nameSchema = z.string()
61
+ .min(1, 'Name is required')
62
+ .max(100, 'Name too long')
63
+ .refine(name => {
64
+ // Check for XSS attempts and other invalid patterns
65
+ const dangerousPatterns = [
66
+ /<script/i,
67
+ /<img/i,
68
+ /on\w+\s*=/i,
69
+ /javascript:/i,
70
+ /data:/i,
71
+ /vbscript:/i
72
+ ];
73
+
74
+ return !dangerousPatterns.some(pattern => pattern.test(name));
75
+ }, 'Name contains invalid characters')
76
+ .transform(name => sanitizeUserInput(name, {
77
+ allowHtml: false,
78
+ maxLength: 100,
79
+ trim: true
80
+ }));
81
+
82
+ /**
83
+ * Phone number validation with sanitization
84
+ */
85
+ export const phoneSchema = z.string()
86
+ .min(10, 'Phone number must be at least 10 digits')
87
+ .max(20, 'Phone number too long')
88
+ .regex(/^[\+]?[0-9\s\-\(\)\.]+$/, 'Invalid phone number format')
89
+ .refine(phone => {
90
+ // Remove all non-digit characters and check length
91
+ const digitsOnly = phone.replace(/\D/g, '');
92
+ return digitsOnly.length >= 10 && digitsOnly.length <= 15;
93
+ }, 'Phone number must be between 10 and 15 digits');
94
+
95
+ /**
96
+ * URL validation with sanitization
97
+ */
98
+ export const urlSchema = z.string()
99
+ .min(1, 'URL is required')
100
+ .max(2048, 'URL too long')
101
+ .refine(url => {
102
+ try {
103
+ const parsed = new URL(url);
104
+ return ['http:', 'https:'].includes(parsed.protocol);
105
+ } catch {
106
+ return false;
107
+ }
108
+ }, 'Invalid URL format')
109
+ .refine(url => {
110
+ // Additional security checks
111
+ const dangerousPatterns = [
112
+ /javascript:/i,
113
+ /data:/i,
114
+ /vbscript:/i,
115
+ /file:/i,
116
+ /mailto:/i
117
+ ];
118
+
119
+ return !dangerousPatterns.some(pattern => pattern.test(url));
120
+ }, 'URL contains invalid protocol');
@@ -4,8 +4,8 @@
4
4
  * @module Validation
5
5
  * @since 0.1.0
6
6
  *
7
- * Re-exports all validation utilities and schemas from utils/validation.
8
- * This module provides a cleaner import path for consumers.
7
+ * Re-export validation utilities from utils/validation for convenience.
8
+ * This provides a top-level validation entry point.
9
9
  */
10
10
 
11
11
  export * from '../utils/validation';
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/security/secureStorage.ts"],"sourcesContent":["\n/**\n * @file Secure Storage Utilities\n * @description Encrypted storage wrapper for sensitive data\n */\n\nexport interface SecureStorageOptions {\n encrypt?: boolean;\n expiry?: number; // TTL in milliseconds\n}\n\n/**\n * Secure storage implementation with encryption support\n */\nclass SecureStorageImpl {\n private encryptionKey: CryptoKey | null = null;\n private initialized = false;\n\n /**\n * Initialize secure storage with encryption\n */\n async init(): Promise<void> {\n if (this.initialized) return;\n\n try {\n // Check if Web Crypto API is available\n if (window.crypto && window.crypto.subtle) {\n // Generate or retrieve encryption key\n const keyData = localStorage.getItem('_sec_key');\n if (keyData) {\n try {\n const keyBuffer = this.base64ToArrayBuffer(keyData);\n this.encryptionKey = await window.crypto.subtle.importKey(\n 'raw',\n keyBuffer,\n { name: 'AES-GCM' },\n false,\n ['encrypt', 'decrypt']\n );\n } catch (error) {\n await this.generateNewKey();\n }\n } else {\n await this.generateNewKey();\n }\n }\n this.initialized = true;\n } catch (error) {\n this.initialized = true;\n }\n }\n\n /**\n * Store item securely\n */\n async setItem(\n key: string,\n value: string,\n options: SecureStorageOptions = {}\n ): Promise<void> {\n await this.init();\n\n const data = {\n value,\n timestamp: Date.now(),\n expiry: options.expiry ? Date.now() + options.expiry : undefined,\n };\n\n const serialized = JSON.stringify(data);\n \n if (options.encrypt && this.encryptionKey) {\n try {\n const encrypted = await this.encrypt(serialized);\n localStorage.setItem(`_sec_${key}`, encrypted);\n return;\n } catch (error) {\n // Silent fail - store as plain text\n }\n }\n\n localStorage.setItem(key, serialized);\n }\n\n /**\n * Retrieve item securely\n */\n async getItem(key: string): Promise<string | null> {\n await this.init();\n\n // Try encrypted storage first\n const encryptedData = localStorage.getItem(`_sec_${key}`);\n if (encryptedData && this.encryptionKey) {\n try {\n const decrypted = await this.decrypt(encryptedData);\n const parsed = JSON.parse(decrypted);\n \n // Check expiry\n if (parsed.expiry && Date.now() > parsed.expiry) {\n await this.removeItem(key);\n return null;\n }\n \n return parsed.value;\n } catch (error) {\n // Silent fail - try plain storage\n }\n }\n\n // Fallback to plain storage\n const plainData = localStorage.getItem(key);\n if (!plainData) return null;\n\n try {\n const parsed = JSON.parse(plainData);\n \n // Check expiry\n if (parsed.expiry && Date.now() > parsed.expiry) {\n await this.removeItem(key);\n return null;\n }\n \n return parsed.value || plainData;\n } catch (error) {\n // If parsing fails, return as-is (backward compatibility)\n return plainData;\n }\n }\n\n /**\n * Remove item\n */\n async removeItem(key: string): Promise<void> {\n localStorage.removeItem(key);\n localStorage.removeItem(`_sec_${key}`);\n }\n\n /**\n * Clear all secure storage\n */\n async clear(): Promise<void> {\n const keys = Object.keys(localStorage);\n for (const key of keys) {\n if (key.startsWith('_sec_')) {\n localStorage.removeItem(key);\n }\n }\n }\n\n /**\n * Generate new encryption key\n */\n private async generateNewKey(): Promise<void> {\n if (!window.crypto?.subtle) return;\n\n try {\n this.encryptionKey = await window.crypto.subtle.generateKey(\n { name: 'AES-GCM', length: 256 },\n true,\n ['encrypt', 'decrypt']\n );\n\n // Export and store key\n const exportedKey = await window.crypto.subtle.exportKey('raw', this.encryptionKey);\n const keyData = this.arrayBufferToBase64(exportedKey);\n localStorage.setItem('_sec_key', keyData);\n } catch (error) {\n // Silent fail - encryption not available\n }\n }\n\n /**\n * Encrypt data\n */\n private async encrypt(data: string): Promise<string> {\n if (!this.encryptionKey || !window.crypto?.subtle) {\n throw new Error('Encryption not available');\n }\n\n const encoder = new TextEncoder();\n const dataBuffer = encoder.encode(data);\n const iv = window.crypto.getRandomValues(new Uint8Array(12));\n\n const encrypted = await window.crypto.subtle.encrypt(\n { name: 'AES-GCM', iv },\n this.encryptionKey,\n dataBuffer\n );\n\n // Combine IV and encrypted data\n const combined = new Uint8Array(iv.length + encrypted.byteLength);\n combined.set(iv);\n combined.set(new Uint8Array(encrypted), iv.length);\n\n return this.arrayBufferToBase64(combined.buffer);\n }\n\n /**\n * Decrypt data\n */\n private async decrypt(encryptedData: string): Promise<string> {\n if (!this.encryptionKey || !window.crypto?.subtle) {\n throw new Error('Decryption not available');\n }\n\n const combined = this.base64ToArrayBuffer(encryptedData);\n const iv = combined.slice(0, 12);\n const encrypted = combined.slice(12);\n\n const decrypted = await window.crypto.subtle.decrypt(\n { name: 'AES-GCM', iv },\n this.encryptionKey,\n encrypted\n );\n\n const decoder = new TextDecoder();\n return decoder.decode(decrypted);\n }\n\n /**\n * Convert ArrayBuffer to base64\n */\n private arrayBufferToBase64(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n }\n\n /**\n * Convert base64 to ArrayBuffer\n */\n private base64ToArrayBuffer(base64: string): ArrayBuffer {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes.buffer;\n }\n}\n\nexport const secureStorage = new SecureStorageImpl();\n"],"mappings":";;;;;AAAA,IAcM,mBAqOO;AAnPb;AAAA;AAAA;AAcA,IAAM,oBAAN,MAAwB;AAAA,MAAxB;AACE,aAAQ,gBAAkC;AAC1C,aAAQ,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,MAKtB,MAAM,OAAsB;AAC1B,YAAI,KAAK,YAAa;AAEtB,YAAI;AAEF,cAAI,OAAO,UAAU,OAAO,OAAO,QAAQ;AAEzC,kBAAM,UAAU,aAAa,QAAQ,UAAU;AAC/C,gBAAI,SAAS;AACX,kBAAI;AACF,sBAAM,YAAY,KAAK,oBAAoB,OAAO;AAClD,qBAAK,gBAAgB,MAAM,OAAO,OAAO,OAAO;AAAA,kBAC9C;AAAA,kBACA;AAAA,kBACA,EAAE,MAAM,UAAU;AAAA,kBAClB;AAAA,kBACA,CAAC,WAAW,SAAS;AAAA,gBACvB;AAAA,cACF,SAAS,OAAO;AACd,sBAAM,KAAK,eAAe;AAAA,cAC5B;AAAA,YACF,OAAO;AACL,oBAAM,KAAK,eAAe;AAAA,YAC5B;AAAA,UACF;AACA,eAAK,cAAc;AAAA,QACrB,SAAS,OAAO;AACd,eAAK,cAAc;AAAA,QACrB;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,QACJ,KACA,OACA,UAAgC,CAAC,GAClB;AACf,cAAM,KAAK,KAAK;AAEhB,cAAM,OAAO;AAAA,UACX;AAAA,UACA,WAAW,KAAK,IAAI;AAAA,UACpB,QAAQ,QAAQ,SAAS,KAAK,IAAI,IAAI,QAAQ,SAAS;AAAA,QACzD;AAEA,cAAM,aAAa,KAAK,UAAU,IAAI;AAEtC,YAAI,QAAQ,WAAW,KAAK,eAAe;AACzC,cAAI;AACF,kBAAM,YAAY,MAAM,KAAK,QAAQ,UAAU;AAC/C,yBAAa,QAAQ,QAAQ,GAAG,IAAI,SAAS;AAC7C;AAAA,UACF,SAAS,OAAO;AAAA,UAEhB;AAAA,QACF;AAEA,qBAAa,QAAQ,KAAK,UAAU;AAAA,MACtC;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,QAAQ,KAAqC;AACjD,cAAM,KAAK,KAAK;AAGhB,cAAM,gBAAgB,aAAa,QAAQ,QAAQ,GAAG,EAAE;AACxD,YAAI,iBAAiB,KAAK,eAAe;AACvC,cAAI;AACF,kBAAM,YAAY,MAAM,KAAK,QAAQ,aAAa;AAClD,kBAAM,SAAS,KAAK,MAAM,SAAS;AAGnC,gBAAI,OAAO,UAAU,KAAK,IAAI,IAAI,OAAO,QAAQ;AAC/C,oBAAM,KAAK,WAAW,GAAG;AACzB,qBAAO;AAAA,YACT;AAEA,mBAAO,OAAO;AAAA,UAChB,SAAS,OAAO;AAAA,UAEhB;AAAA,QACF;AAGA,cAAM,YAAY,aAAa,QAAQ,GAAG;AAC1C,YAAI,CAAC,UAAW,QAAO;AAEvB,YAAI;AACF,gBAAM,SAAS,KAAK,MAAM,SAAS;AAGnC,cAAI,OAAO,UAAU,KAAK,IAAI,IAAI,OAAO,QAAQ;AAC/C,kBAAM,KAAK,WAAW,GAAG;AACzB,mBAAO;AAAA,UACT;AAEA,iBAAO,OAAO,SAAS;AAAA,QACzB,SAAS,OAAO;AAEd,iBAAO;AAAA,QACT;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,WAAW,KAA4B;AAC3C,qBAAa,WAAW,GAAG;AAC3B,qBAAa,WAAW,QAAQ,GAAG,EAAE;AAAA,MACvC;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,QAAuB;AAC3B,cAAM,OAAO,OAAO,KAAK,YAAY;AACrC,mBAAW,OAAO,MAAM;AACtB,cAAI,IAAI,WAAW,OAAO,GAAG;AAC3B,yBAAa,WAAW,GAAG;AAAA,UAC7B;AAAA,QACF;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,MAAc,iBAAgC;AAC5C,YAAI,CAAC,OAAO,QAAQ,OAAQ;AAE5B,YAAI;AACF,eAAK,gBAAgB,MAAM,OAAO,OAAO,OAAO;AAAA,YAC9C,EAAE,MAAM,WAAW,QAAQ,IAAI;AAAA,YAC/B;AAAA,YACA,CAAC,WAAW,SAAS;AAAA,UACvB;AAGA,gBAAM,cAAc,MAAM,OAAO,OAAO,OAAO,UAAU,OAAO,KAAK,aAAa;AAClF,gBAAM,UAAU,KAAK,oBAAoB,WAAW;AACpD,uBAAa,QAAQ,YAAY,OAAO;AAAA,QAC1C,SAAS,OAAO;AAAA,QAEhB;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,MAAc,QAAQ,MAA+B;AACnD,YAAI,CAAC,KAAK,iBAAiB,CAAC,OAAO,QAAQ,QAAQ;AACjD,gBAAM,IAAI,MAAM,0BAA0B;AAAA,QAC5C;AAEA,cAAM,UAAU,IAAI,YAAY;AAChC,cAAM,aAAa,QAAQ,OAAO,IAAI;AACtC,cAAM,KAAK,OAAO,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC;AAE3D,cAAM,YAAY,MAAM,OAAO,OAAO,OAAO;AAAA,UAC3C,EAAE,MAAM,WAAW,GAAG;AAAA,UACtB,KAAK;AAAA,UACL;AAAA,QACF;AAGA,cAAM,WAAW,IAAI,WAAW,GAAG,SAAS,UAAU,UAAU;AAChE,iBAAS,IAAI,EAAE;AACf,iBAAS,IAAI,IAAI,WAAW,SAAS,GAAG,GAAG,MAAM;AAEjD,eAAO,KAAK,oBAAoB,SAAS,MAAM;AAAA,MACjD;AAAA;AAAA;AAAA;AAAA,MAKA,MAAc,QAAQ,eAAwC;AAC5D,YAAI,CAAC,KAAK,iBAAiB,CAAC,OAAO,QAAQ,QAAQ;AACjD,gBAAM,IAAI,MAAM,0BAA0B;AAAA,QAC5C;AAEA,cAAM,WAAW,KAAK,oBAAoB,aAAa;AACvD,cAAM,KAAK,SAAS,MAAM,GAAG,EAAE;AAC/B,cAAM,YAAY,SAAS,MAAM,EAAE;AAEnC,cAAM,YAAY,MAAM,OAAO,OAAO,OAAO;AAAA,UAC3C,EAAE,MAAM,WAAW,GAAG;AAAA,UACtB,KAAK;AAAA,UACL;AAAA,QACF;AAEA,cAAM,UAAU,IAAI,YAAY;AAChC,eAAO,QAAQ,OAAO,SAAS;AAAA,MACjC;AAAA;AAAA;AAAA;AAAA,MAKQ,oBAAoB,QAA6B;AACvD,cAAM,QAAQ,IAAI,WAAW,MAAM;AACnC,YAAI,SAAS;AACb,iBAAS,IAAI,GAAG,IAAI,MAAM,YAAY,KAAK;AACzC,oBAAU,OAAO,aAAa,MAAM,CAAC,CAAC;AAAA,QACxC;AACA,eAAO,KAAK,MAAM;AAAA,MACpB;AAAA;AAAA;AAAA;AAAA,MAKQ,oBAAoB,QAA6B;AACvD,cAAM,SAAS,KAAK,MAAM;AAC1B,cAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;AAC1C,iBAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,gBAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AAAA,QAChC;AACA,eAAO,MAAM;AAAA,MACf;AAAA,IACF;AAEO,IAAM,gBAAgB,IAAI,kBAAkB;AAAA;AAAA;","names":[]}