@jmruthers/pace-core 0.5.121 → 0.5.124

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 (255) hide show
  1. package/dist/{AuthService-D4646R4b.d.ts → AuthService-DYuQPJj6.d.ts} +0 -9
  2. package/dist/{DataTable-DGZDJUYM.js → DataTable-OKDYRW2S.js} +7 -8
  3. package/dist/{PublicLoadingSpinner-DgDWTFqn.d.ts → PublicLoadingSpinner-CaoRbHvJ.d.ts} +30 -4
  4. package/dist/{UnifiedAuthProvider-UACKFATV.js → UnifiedAuthProvider-6C47WIML.js} +3 -4
  5. package/dist/{chunk-D6BOFXYR.js → chunk-35ZDPMBM.js} +3 -3
  6. package/dist/{chunk-CGURJ27Z.js → chunk-4MXVZVNS.js} +2 -2
  7. package/dist/{chunk-ZYJ6O5CA.js → chunk-C43QIDN3.js} +2 -2
  8. package/dist/{chunk-VKOCWWVY.js → chunk-CX5M4ZAG.js} +1 -6
  9. package/dist/{chunk-VKOCWWVY.js 3.map → chunk-CX5M4ZAG.js.map} +1 -1
  10. package/dist/{chunk-RIEJGKD3.js → chunk-ESJTIADP.js} +15 -6
  11. package/dist/{chunk-RIEJGKD3.js.map → chunk-ESJTIADP.js.map} +1 -1
  12. package/dist/{chunk-HFBOFZ3Z.js → chunk-GBGYYMC6.js} +317 -251
  13. package/dist/chunk-GBGYYMC6.js.map +1 -0
  14. package/dist/{chunk-SMJZMKYN.js → chunk-GEVIB2UB.js} +43 -10
  15. package/dist/chunk-GEVIB2UB.js.map +1 -0
  16. package/dist/{chunk-TDNI6ZWL.js → chunk-IJOZZOGT.js} +7 -7
  17. package/dist/chunk-IJOZZOGT.js.map +1 -0
  18. package/dist/{chunk-GZRXOUBE.js → chunk-M6DDYFUD.js} +2 -2
  19. package/dist/chunk-M6DDYFUD.js.map +1 -0
  20. package/dist/{chunk-B4GZ2BXO.js → chunk-NZGLXZGP.js} +3 -3
  21. package/dist/{chunk-NZ32EONV.js → chunk-QWNJCQXZ.js} +2 -2
  22. package/dist/{chunk-QPI2CCBA.js → chunk-VPUCTHTY.js} +149 -96
  23. package/dist/chunk-VPUCTHTY.js.map +1 -0
  24. package/dist/{chunk-FKFHZUGF.js → chunk-XN6GWKMV.js} +43 -56
  25. package/dist/chunk-XN6GWKMV.js.map +1 -0
  26. package/dist/{chunk-BHWIUEYH.js → chunk-ZBLK676C.js} +1 -61
  27. package/dist/chunk-ZBLK676C.js.map +1 -0
  28. package/dist/components.d.ts +1 -1
  29. package/dist/components.js +11 -11
  30. package/dist/{formatting-B1jSqgl-.d.ts → formatting-DFcCxUEk.d.ts} +1 -1
  31. package/dist/hooks.d.ts +1 -1
  32. package/dist/hooks.js +9 -8
  33. package/dist/hooks.js.map +1 -1
  34. package/dist/index.d.ts +6 -6
  35. package/dist/index.js +19 -17
  36. package/dist/index.js.map +1 -1
  37. package/dist/providers.d.ts +2 -2
  38. package/dist/providers.js +2 -3
  39. package/dist/rbac/index.js +7 -8
  40. package/dist/styles/index.d.ts +1 -1
  41. package/dist/styles/index.js +5 -3
  42. package/dist/theming/runtime.d.ts +73 -1
  43. package/dist/theming/runtime.js +5 -5
  44. package/dist/{usePublicRouteParams-BdF8bZgs.d.ts → usePublicRouteParams-Dyt1tzI9.d.ts} +60 -8
  45. package/dist/utils.d.ts +1 -1
  46. package/dist/utils.js +5 -5
  47. package/docs/api/classes/ColumnFactory.md +1 -1
  48. package/docs/api/classes/ErrorBoundary.md +1 -1
  49. package/docs/api/classes/InvalidScopeError.md +1 -1
  50. package/docs/api/classes/MissingUserContextError.md +1 -1
  51. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  52. package/docs/api/classes/PermissionDeniedError.md +1 -1
  53. package/docs/api/classes/PublicErrorBoundary.md +6 -6
  54. package/docs/api/classes/RBACAuditManager.md +1 -1
  55. package/docs/api/classes/RBACCache.md +1 -1
  56. package/docs/api/classes/RBACEngine.md +1 -1
  57. package/docs/api/classes/RBACError.md +1 -1
  58. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  59. package/docs/api/classes/SecureSupabaseClient.md +6 -6
  60. package/docs/api/classes/StorageUtils.md +1 -1
  61. package/docs/api/enums/FileCategory.md +1 -1
  62. package/docs/api/interfaces/AggregateConfig.md +1 -1
  63. package/docs/api/interfaces/ButtonProps.md +1 -1
  64. package/docs/api/interfaces/CardProps.md +1 -1
  65. package/docs/api/interfaces/ColorPalette.md +1 -1
  66. package/docs/api/interfaces/ColorShade.md +1 -1
  67. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  68. package/docs/api/interfaces/DataRecord.md +1 -1
  69. package/docs/api/interfaces/DataTableAction.md +1 -1
  70. package/docs/api/interfaces/DataTableColumn.md +1 -1
  71. package/docs/api/interfaces/DataTableProps.md +1 -1
  72. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  73. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  74. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  75. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  76. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  77. package/docs/api/interfaces/FileMetadata.md +1 -1
  78. package/docs/api/interfaces/FileReference.md +1 -1
  79. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  80. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  81. package/docs/api/interfaces/FileUploadProps.md +1 -1
  82. package/docs/api/interfaces/FooterProps.md +1 -1
  83. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  84. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  85. package/docs/api/interfaces/InputProps.md +1 -1
  86. package/docs/api/interfaces/LabelProps.md +1 -1
  87. package/docs/api/interfaces/LoginFormProps.md +1 -1
  88. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  89. package/docs/api/interfaces/NavigationContextType.md +1 -1
  90. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  91. package/docs/api/interfaces/NavigationItem.md +1 -1
  92. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  93. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  94. package/docs/api/interfaces/Organisation.md +1 -1
  95. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  96. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  97. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  98. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  99. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  100. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  101. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  102. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  103. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  104. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  105. package/docs/api/interfaces/PaletteData.md +1 -1
  106. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  107. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  108. package/docs/api/interfaces/PublicErrorBoundaryProps.md +7 -7
  109. package/docs/api/interfaces/PublicErrorBoundaryState.md +5 -5
  110. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +7 -7
  111. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  112. package/docs/api/interfaces/PublicPageHeaderProps.md +51 -12
  113. package/docs/api/interfaces/PublicPageLayoutProps.md +72 -12
  114. package/docs/api/interfaces/RBACConfig.md +1 -1
  115. package/docs/api/interfaces/RBACLogger.md +1 -1
  116. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  117. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  118. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  119. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  120. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  121. package/docs/api/interfaces/RouteConfig.md +1 -1
  122. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  123. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  124. package/docs/api/interfaces/StorageConfig.md +1 -1
  125. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  126. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  127. package/docs/api/interfaces/StorageListOptions.md +1 -1
  128. package/docs/api/interfaces/StorageListResult.md +1 -1
  129. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  130. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  131. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  132. package/docs/api/interfaces/StyleImport.md +1 -1
  133. package/docs/api/interfaces/SwitchProps.md +1 -1
  134. package/docs/api/interfaces/ToastActionElement.md +1 -1
  135. package/docs/api/interfaces/ToastProps.md +1 -1
  136. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  137. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  138. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  139. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  140. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  141. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  142. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  143. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  144. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  145. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  146. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  147. package/docs/api/interfaces/UserEventAccess.md +1 -1
  148. package/docs/api/interfaces/UserMenuProps.md +1 -1
  149. package/docs/api/interfaces/UserProfile.md +1 -1
  150. package/docs/api/modules.md +140 -30
  151. package/docs/best-practices/README.md +1 -1
  152. package/docs/implementation-guides/datatable-filtering.md +313 -0
  153. package/docs/implementation-guides/datatable-rbac-usage.md +317 -0
  154. package/docs/implementation-guides/hierarchical-datatable.md +850 -0
  155. package/docs/implementation-guides/large-datasets.md +281 -0
  156. package/docs/implementation-guides/performance.md +403 -0
  157. package/docs/implementation-guides/public-pages.md +4 -4
  158. package/docs/migration/quick-migration-guide.md +320 -0
  159. package/docs/rbac/quick-start.md +16 -16
  160. package/docs/troubleshooting/README.md +4 -4
  161. package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +1 -1
  162. package/docs/troubleshooting/debugging.md +1117 -0
  163. package/docs/troubleshooting/migration.md +918 -0
  164. package/examples/public-pages/CorrectPublicPageImplementation.tsx +30 -30
  165. package/examples/public-pages/PublicEventPage.tsx +41 -41
  166. package/examples/public-pages/PublicPageApp.tsx +33 -33
  167. package/examples/public-pages/PublicPageUsageExample.tsx +30 -30
  168. package/package.json +4 -4
  169. package/src/__tests__/hooks/usePermissions.test.ts +265 -0
  170. package/src/components/DataTable/DataTable.test.tsx +9 -38
  171. package/src/components/DataTable/DataTable.tsx +0 -7
  172. package/src/components/DataTable/components/DataTableCore.tsx +125 -144
  173. package/src/components/DataTable/components/DataTableModals.tsx +25 -22
  174. package/src/components/DataTable/components/DataTableToolbar.tsx +14 -1
  175. package/src/components/DataTable/components/EditableRow.tsx +118 -42
  176. package/src/components/DataTable/components/UnifiedTableBody.tsx +129 -76
  177. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +33 -14
  178. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +17 -5
  179. package/src/components/DataTable/utils/exportUtils.ts +3 -2
  180. package/src/components/Dialog/Dialog.tsx +1 -1
  181. package/src/components/Dialog/README.md +24 -24
  182. package/src/components/Dialog/examples/BasicHtmlTest.tsx +2 -2
  183. package/src/components/Dialog/examples/DebugHtmlExample.tsx +6 -6
  184. package/src/components/Dialog/examples/HtmlDialogExample.tsx +2 -2
  185. package/src/components/Dialog/examples/SimpleHtmlTest.tsx +3 -3
  186. package/src/components/Dialog/examples/__tests__/SimpleHtmlTest.test.tsx +4 -4
  187. package/src/components/PaceAppLayout/PaceAppLayout.tsx +12 -1
  188. package/src/components/PublicLayout/EventLogo.tsx +175 -0
  189. package/src/components/PublicLayout/PublicErrorBoundary.tsx +22 -18
  190. package/src/components/PublicLayout/PublicLoadingSpinner.tsx +22 -14
  191. package/src/components/PublicLayout/PublicPageHeader.tsx +133 -40
  192. package/src/components/PublicLayout/PublicPageLayout.tsx +75 -72
  193. package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +1 -1
  194. package/src/components/PublicLayout/__tests__/PublicLoadingSpinner.test.tsx +8 -8
  195. package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +23 -16
  196. package/src/components/PublicLayout/__tests__/PublicPageLayout.test.tsx +86 -14
  197. package/src/examples/CorrectPublicPageImplementation.tsx +30 -30
  198. package/src/examples/PublicEventPage.tsx +41 -41
  199. package/src/examples/PublicPageApp.tsx +33 -33
  200. package/src/examples/PublicPageUsageExample.tsx +30 -30
  201. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +583 -0
  202. package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +10 -3
  203. package/src/hooks/index.ts +1 -1
  204. package/src/hooks/public/usePublicEventLogo.ts +285 -0
  205. package/src/hooks/public/usePublicRouteParams.ts +21 -4
  206. package/src/hooks/useEventTheme.test.ts +119 -43
  207. package/src/hooks/useEventTheme.ts +84 -55
  208. package/src/index.ts +3 -1
  209. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +630 -0
  210. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +667 -0
  211. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +647 -0
  212. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +496 -0
  213. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +496 -0
  214. package/src/rbac/secureClient.ts +4 -2
  215. package/src/services/EventService.ts +0 -66
  216. package/src/services/__tests__/EventService.eventColours.test.ts +44 -40
  217. package/src/styles/index.ts +1 -1
  218. package/src/theming/__tests__/parseEventColours.test.ts +209 -0
  219. package/src/theming/parseEventColours.ts +123 -0
  220. package/src/theming/runtime.ts +3 -0
  221. package/src/types/__tests__/file-reference.test.ts +447 -0
  222. package/src/utils/formatDate.test.ts +11 -11
  223. package/src/utils/formatting.ts +3 -2
  224. package/dist/chunk-BDZUMRBD.js 3.map +0 -1
  225. package/dist/chunk-BHWIUEYH.js.map +0 -1
  226. package/dist/chunk-CGURJ27Z.js.map +0 -1
  227. package/dist/chunk-FKFHZUGF.js.map +0 -1
  228. package/dist/chunk-GKHF54DI 2.js +0 -619
  229. package/dist/chunk-GKHF54DI.js 2.map +0 -1
  230. package/dist/chunk-GZRXOUBE.js.map +0 -1
  231. package/dist/chunk-HFBOFZ3Z.js.map +0 -1
  232. package/dist/chunk-NZ32EONV.js.map +0 -1
  233. package/dist/chunk-O3NWNXDY 2.js +0 -76
  234. package/dist/chunk-QPI2CCBA.js.map +0 -1
  235. package/dist/chunk-SMJZMKYN.js.map +0 -1
  236. package/dist/chunk-TDNI6ZWL.js 2.map +0 -1
  237. package/dist/chunk-TDNI6ZWL.js.map +0 -1
  238. package/dist/chunk-VKOCWWVY.js.map +0 -1
  239. package/dist/chunk-WP5I5GLN 2.js +0 -1564
  240. package/dist/index 3.js +0 -856
  241. package/dist/providers 3.js +0 -38
  242. package/dist/providers.js 3.map +0 -1
  243. package/dist/types 3.js +0 -128
  244. package/dist/types.js 3.map +0 -1
  245. package/dist/useInactivityTracker-MRUU55XI.js 3.map +0 -1
  246. package/dist/utils.js 3.map +0 -1
  247. package/dist/validation 3.js +0 -479
  248. package/src/styles/semantic.css +0 -24
  249. /package/dist/{DataTable-DGZDJUYM.js.map → DataTable-OKDYRW2S.js.map} +0 -0
  250. /package/dist/{UnifiedAuthProvider-UACKFATV.js.map → UnifiedAuthProvider-6C47WIML.js.map} +0 -0
  251. /package/dist/{chunk-D6BOFXYR.js.map → chunk-35ZDPMBM.js.map} +0 -0
  252. /package/dist/{chunk-CGURJ27Z.js 2.map → chunk-4MXVZVNS.js.map} +0 -0
  253. /package/dist/{chunk-ZYJ6O5CA.js.map → chunk-C43QIDN3.js.map} +0 -0
  254. /package/dist/{chunk-B4GZ2BXO.js.map → chunk-NZGLXZGP.js.map} +0 -0
  255. /package/dist/{chunk-NZ32EONV.js 2.map → chunk-QWNJCQXZ.js.map} +0 -0
@@ -0,0 +1,447 @@
1
+ /**
2
+ * @file File Reference Type Tests
3
+ * @package @jmruthers/pace-core
4
+ * @module Types/__tests__
5
+ * @since 1.0.0
6
+ */
7
+
8
+ import { describe, it, expect } from 'vitest';
9
+ import type {
10
+ FileReference,
11
+ FileUploadOptions,
12
+ FileUploadResult,
13
+ FileValidationError,
14
+ FileMetadata,
15
+ FileStorageConfig,
16
+ FileAccessLevel
17
+ } from '../file-reference';
18
+
19
+ describe('[types] File Reference Types', () => {
20
+ describe('FileReference interface', () => {
21
+ it('validates FileReference type structure', () => {
22
+ const fileRef: FileReference = {
23
+ id: 'test-file-id',
24
+ filename: 'test-file.pdf',
25
+ originalName: 'Test File.pdf',
26
+ mimeType: 'application/pdf',
27
+ size: 1024,
28
+ url: 'https://example.com/files/test-file.pdf',
29
+ uploadedAt: '2024-01-01T00:00:00Z',
30
+ uploadedBy: 'user-123',
31
+ organisationId: 'org-123',
32
+ eventId: 'event-123',
33
+ accessLevel: 'private',
34
+ metadata: {
35
+ description: 'Test file description',
36
+ tags: ['document', 'test']
37
+ }
38
+ };
39
+
40
+ expect(fileRef).toHaveProperty('id');
41
+ expect(fileRef).toHaveProperty('filename');
42
+ expect(fileRef).toHaveProperty('originalName');
43
+ expect(fileRef).toHaveProperty('mimeType');
44
+ expect(fileRef).toHaveProperty('size');
45
+ expect(fileRef).toHaveProperty('url');
46
+ expect(fileRef).toHaveProperty('uploadedAt');
47
+ expect(fileRef).toHaveProperty('uploadedBy');
48
+ expect(fileRef).toHaveProperty('organisationId');
49
+ expect(fileRef).toHaveProperty('eventId');
50
+ expect(fileRef).toHaveProperty('accessLevel');
51
+ expect(fileRef).toHaveProperty('metadata');
52
+
53
+ expect(typeof fileRef.id).toBe('string');
54
+ expect(typeof fileRef.filename).toBe('string');
55
+ expect(typeof fileRef.originalName).toBe('string');
56
+ expect(typeof fileRef.mimeType).toBe('string');
57
+ expect(typeof fileRef.size).toBe('number');
58
+ expect(typeof fileRef.url).toBe('string');
59
+ expect(typeof fileRef.uploadedAt).toBe('string');
60
+ expect(typeof fileRef.uploadedBy).toBe('string');
61
+ expect(typeof fileRef.organisationId).toBe('string');
62
+ expect(typeof fileRef.eventId).toBe('string');
63
+ expect(typeof fileRef.accessLevel).toBe('string');
64
+ expect(typeof fileRef.metadata).toBe('object');
65
+ });
66
+
67
+ it('validates FileReference with minimal required fields', () => {
68
+ const fileRef: FileReference = {
69
+ id: 'test-file-id',
70
+ filename: 'test-file.pdf',
71
+ originalName: 'Test File.pdf',
72
+ mimeType: 'application/pdf',
73
+ size: 1024,
74
+ url: 'https://example.com/files/test-file.pdf',
75
+ uploadedAt: '2024-01-01T00:00:00Z',
76
+ uploadedBy: 'user-123',
77
+ organisationId: 'org-123',
78
+ accessLevel: 'private'
79
+ };
80
+
81
+ expect(fileRef).toHaveProperty('id');
82
+ expect(fileRef).toHaveProperty('filename');
83
+ expect(fileRef.eventId).toBeUndefined();
84
+ expect(fileRef.metadata).toBeUndefined();
85
+ });
86
+ });
87
+
88
+ describe('FileUploadOptions interface', () => {
89
+ it('validates FileUploadOptions type structure', () => {
90
+ const uploadOptions: FileUploadOptions = {
91
+ maxSize: 5 * 1024 * 1024, // 5MB
92
+ allowedTypes: ['image/jpeg', 'image/png', 'application/pdf'],
93
+ allowedExtensions: ['.jpg', '.jpeg', '.png', '.pdf'],
94
+ generateThumbnail: true,
95
+ compressImage: true,
96
+ quality: 0.8,
97
+ resize: {
98
+ width: 1920,
99
+ height: 1080,
100
+ maintainAspectRatio: true
101
+ },
102
+ metadata: {
103
+ description: 'Uploaded file',
104
+ tags: ['upload']
105
+ },
106
+ accessLevel: 'private',
107
+ organisationId: 'org-123',
108
+ eventId: 'event-123'
109
+ };
110
+
111
+ expect(uploadOptions).toHaveProperty('maxSize');
112
+ expect(uploadOptions).toHaveProperty('allowedTypes');
113
+ expect(uploadOptions).toHaveProperty('allowedExtensions');
114
+ expect(uploadOptions).toHaveProperty('generateThumbnail');
115
+ expect(uploadOptions).toHaveProperty('compressImage');
116
+ expect(uploadOptions).toHaveProperty('quality');
117
+ expect(uploadOptions).toHaveProperty('resize');
118
+ expect(uploadOptions).toHaveProperty('metadata');
119
+ expect(uploadOptions).toHaveProperty('accessLevel');
120
+ expect(uploadOptions).toHaveProperty('organisationId');
121
+ expect(uploadOptions).toHaveProperty('eventId');
122
+
123
+ expect(typeof uploadOptions.maxSize).toBe('number');
124
+ expect(Array.isArray(uploadOptions.allowedTypes)).toBe(true);
125
+ expect(Array.isArray(uploadOptions.allowedExtensions)).toBe(true);
126
+ expect(typeof uploadOptions.generateThumbnail).toBe('boolean');
127
+ expect(typeof uploadOptions.compressImage).toBe('boolean');
128
+ expect(typeof uploadOptions.quality).toBe('number');
129
+ expect(typeof uploadOptions.resize).toBe('object');
130
+ expect(typeof uploadOptions.metadata).toBe('object');
131
+ expect(typeof uploadOptions.accessLevel).toBe('string');
132
+ expect(typeof uploadOptions.organisationId).toBe('string');
133
+ expect(typeof uploadOptions.eventId).toBe('string');
134
+ });
135
+
136
+ it('validates FileUploadOptions with minimal fields', () => {
137
+ const uploadOptions: FileUploadOptions = {
138
+ maxSize: 1024 * 1024, // 1MB
139
+ allowedTypes: ['image/jpeg'],
140
+ accessLevel: 'public'
141
+ };
142
+
143
+ expect(uploadOptions).toHaveProperty('maxSize');
144
+ expect(uploadOptions).toHaveProperty('allowedTypes');
145
+ expect(uploadOptions).toHaveProperty('accessLevel');
146
+ expect(uploadOptions.allowedExtensions).toBeUndefined();
147
+ expect(uploadOptions.generateThumbnail).toBeUndefined();
148
+ expect(uploadOptions.compressImage).toBeUndefined();
149
+ });
150
+ });
151
+
152
+ describe('FileUploadResult interface', () => {
153
+ it('validates FileUploadResult success structure', () => {
154
+ const successResult: FileUploadResult = {
155
+ success: true,
156
+ file: {
157
+ id: 'test-file-id',
158
+ filename: 'test-file.pdf',
159
+ originalName: 'Test File.pdf',
160
+ mimeType: 'application/pdf',
161
+ size: 1024,
162
+ url: 'https://example.com/files/test-file.pdf',
163
+ uploadedAt: '2024-01-01T00:00:00Z',
164
+ uploadedBy: 'user-123',
165
+ organisationId: 'org-123',
166
+ accessLevel: 'private'
167
+ },
168
+ thumbnailUrl: 'https://example.com/thumbnails/test-file.jpg',
169
+ message: 'File uploaded successfully'
170
+ };
171
+
172
+ expect(successResult).toHaveProperty('success');
173
+ expect(successResult).toHaveProperty('file');
174
+ expect(successResult).toHaveProperty('thumbnailUrl');
175
+ expect(successResult).toHaveProperty('message');
176
+ expect(successResult.success).toBe(true);
177
+ expect(typeof successResult.file).toBe('object');
178
+ expect(typeof successResult.thumbnailUrl).toBe('string');
179
+ expect(typeof successResult.message).toBe('string');
180
+ });
181
+
182
+ it('validates FileUploadResult error structure', () => {
183
+ const errorResult: FileUploadResult = {
184
+ success: false,
185
+ error: 'File size exceeds maximum allowed size',
186
+ code: 'FILE_TOO_LARGE',
187
+ details: {
188
+ maxSize: 5 * 1024 * 1024,
189
+ actualSize: 10 * 1024 * 1024
190
+ }
191
+ };
192
+
193
+ expect(errorResult).toHaveProperty('success');
194
+ expect(errorResult).toHaveProperty('error');
195
+ expect(errorResult).toHaveProperty('code');
196
+ expect(errorResult).toHaveProperty('details');
197
+ expect(errorResult.success).toBe(false);
198
+ expect(typeof errorResult.error).toBe('string');
199
+ expect(typeof errorResult.code).toBe('string');
200
+ expect(typeof errorResult.details).toBe('object');
201
+ expect(errorResult.file).toBeUndefined();
202
+ });
203
+ });
204
+
205
+ describe('FileValidationError interface', () => {
206
+ it('validates FileValidationError type structure', () => {
207
+ const validationError: FileValidationError = {
208
+ field: 'file',
209
+ message: 'File type not allowed',
210
+ code: 'INVALID_FILE_TYPE',
211
+ value: 'test.exe',
212
+ allowedValues: ['image/jpeg', 'image/png', 'application/pdf']
213
+ };
214
+
215
+ expect(validationError).toHaveProperty('field');
216
+ expect(validationError).toHaveProperty('message');
217
+ expect(validationError).toHaveProperty('code');
218
+ expect(validationError).toHaveProperty('value');
219
+ expect(validationError).toHaveProperty('allowedValues');
220
+ expect(typeof validationError.field).toBe('string');
221
+ expect(typeof validationError.message).toBe('string');
222
+ expect(typeof validationError.code).toBe('string');
223
+ expect(typeof validationError.value).toBe('string');
224
+ expect(Array.isArray(validationError.allowedValues)).toBe(true);
225
+ });
226
+ });
227
+
228
+ describe('FileMetadata interface', () => {
229
+ it('validates FileMetadata type structure', () => {
230
+ const metadata: FileMetadata = {
231
+ description: 'Test file description',
232
+ tags: ['document', 'test', 'important'],
233
+ category: 'documents',
234
+ author: 'John Doe',
235
+ version: '1.0',
236
+ customFields: {
237
+ department: 'IT',
238
+ project: 'Test Project',
239
+ priority: 'high'
240
+ }
241
+ };
242
+
243
+ expect(metadata).toHaveProperty('description');
244
+ expect(metadata).toHaveProperty('tags');
245
+ expect(metadata).toHaveProperty('category');
246
+ expect(metadata).toHaveProperty('author');
247
+ expect(metadata).toHaveProperty('version');
248
+ expect(metadata).toHaveProperty('customFields');
249
+ expect(typeof metadata.description).toBe('string');
250
+ expect(Array.isArray(metadata.tags)).toBe(true);
251
+ expect(typeof metadata.category).toBe('string');
252
+ expect(typeof metadata.author).toBe('string');
253
+ expect(typeof metadata.version).toBe('string');
254
+ expect(typeof metadata.customFields).toBe('object');
255
+ });
256
+
257
+ it('validates FileMetadata with minimal fields', () => {
258
+ const metadata: FileMetadata = {
259
+ description: 'Test file'
260
+ };
261
+
262
+ expect(metadata).toHaveProperty('description');
263
+ expect(metadata.tags).toBeUndefined();
264
+ expect(metadata.category).toBeUndefined();
265
+ expect(metadata.author).toBeUndefined();
266
+ expect(metadata.version).toBeUndefined();
267
+ expect(metadata.customFields).toBeUndefined();
268
+ });
269
+ });
270
+
271
+ describe('FileStorageConfig interface', () => {
272
+ it('validates FileStorageConfig type structure', () => {
273
+ const storageConfig: FileStorageConfig = {
274
+ provider: 'supabase',
275
+ bucket: 'pace-files',
276
+ region: 'us-east-1',
277
+ cdnUrl: 'https://cdn.example.com',
278
+ maxFileSize: 10 * 1024 * 1024, // 10MB
279
+ allowedMimeTypes: ['image/*', 'application/pdf', 'text/*'],
280
+ compressionEnabled: true,
281
+ encryptionEnabled: true,
282
+ retentionPolicy: {
283
+ days: 365,
284
+ autoDelete: true
285
+ }
286
+ };
287
+
288
+ expect(storageConfig).toHaveProperty('provider');
289
+ expect(storageConfig).toHaveProperty('bucket');
290
+ expect(storageConfig).toHaveProperty('region');
291
+ expect(storageConfig).toHaveProperty('cdnUrl');
292
+ expect(storageConfig).toHaveProperty('maxFileSize');
293
+ expect(storageConfig).toHaveProperty('allowedMimeTypes');
294
+ expect(storageConfig).toHaveProperty('compressionEnabled');
295
+ expect(storageConfig).toHaveProperty('encryptionEnabled');
296
+ expect(storageConfig).toHaveProperty('retentionPolicy');
297
+ expect(typeof storageConfig.provider).toBe('string');
298
+ expect(typeof storageConfig.bucket).toBe('string');
299
+ expect(typeof storageConfig.region).toBe('string');
300
+ expect(typeof storageConfig.cdnUrl).toBe('string');
301
+ expect(typeof storageConfig.maxFileSize).toBe('number');
302
+ expect(Array.isArray(storageConfig.allowedMimeTypes)).toBe(true);
303
+ expect(typeof storageConfig.compressionEnabled).toBe('boolean');
304
+ expect(typeof storageConfig.encryptionEnabled).toBe('boolean');
305
+ expect(typeof storageConfig.retentionPolicy).toBe('object');
306
+ });
307
+ });
308
+
309
+ describe('FileAccessLevel type', () => {
310
+ it('validates FileAccessLevel values', () => {
311
+ const accessLevels: FileAccessLevel[] = ['public', 'private', 'restricted'];
312
+
313
+ expect(accessLevels).toContain('public');
314
+ expect(accessLevels).toContain('private');
315
+ expect(accessLevels).toContain('restricted');
316
+ });
317
+
318
+ it('validates FileAccessLevel type usage', () => {
319
+ const accessLevel: FileAccessLevel = 'private';
320
+ expect(typeof accessLevel).toBe('string');
321
+ expect(['public', 'private', 'restricted']).toContain(accessLevel);
322
+ });
323
+ });
324
+ });
325
+
326
+ describe('[types] File Reference Usage Patterns', () => {
327
+ describe('File Upload Flow', () => {
328
+ it('validates complete file upload flow types', () => {
329
+ interface FileUploadFlow {
330
+ file: File;
331
+ options: FileUploadOptions;
332
+ onProgress: (progress: number) => void;
333
+ onSuccess: (result: FileUploadResult) => void;
334
+ onError: (error: FileValidationError) => void;
335
+ }
336
+
337
+ const uploadFlow: FileUploadFlow = {
338
+ file: new File(['test'], 'test.txt', { type: 'text/plain' }),
339
+ options: {
340
+ maxSize: 1024 * 1024,
341
+ allowedTypes: ['text/plain'],
342
+ accessLevel: 'private'
343
+ },
344
+ onProgress: (progress) => {
345
+ expect(typeof progress).toBe('number');
346
+ expect(progress).toBeGreaterThanOrEqual(0);
347
+ expect(progress).toBeLessThanOrEqual(100);
348
+ },
349
+ onSuccess: (result) => {
350
+ expect(result).toHaveProperty('success');
351
+ expect(result).toHaveProperty('file');
352
+ },
353
+ onError: (error) => {
354
+ expect(error).toHaveProperty('field');
355
+ expect(error).toHaveProperty('message');
356
+ expect(error).toHaveProperty('code');
357
+ }
358
+ };
359
+
360
+ expect(uploadFlow).toHaveProperty('file');
361
+ expect(uploadFlow).toHaveProperty('options');
362
+ expect(uploadFlow).toHaveProperty('onProgress');
363
+ expect(uploadFlow).toHaveProperty('onSuccess');
364
+ expect(uploadFlow).toHaveProperty('onError');
365
+ expect(uploadFlow.file).toBeInstanceOf(File);
366
+ expect(typeof uploadFlow.options).toBe('object');
367
+ expect(typeof uploadFlow.onProgress).toBe('function');
368
+ expect(typeof uploadFlow.onSuccess).toBe('function');
369
+ expect(typeof uploadFlow.onError).toBe('function');
370
+ });
371
+ });
372
+
373
+ describe('File Management Operations', () => {
374
+ it('validates file management operation types', () => {
375
+ interface FileManagementOperation {
376
+ operation: 'upload' | 'download' | 'delete' | 'update' | 'list';
377
+ fileId?: string;
378
+ options?: FileUploadOptions;
379
+ metadata?: FileMetadata;
380
+ }
381
+
382
+ const operations: FileManagementOperation[] = [
383
+ { operation: 'upload', options: { maxSize: 1024, allowedTypes: ['image/*'] } },
384
+ { operation: 'download', fileId: 'file-123' },
385
+ { operation: 'delete', fileId: 'file-123' },
386
+ { operation: 'update', fileId: 'file-123', metadata: { description: 'Updated' } },
387
+ { operation: 'list' }
388
+ ];
389
+
390
+ operations.forEach(op => {
391
+ expect(op).toHaveProperty('operation');
392
+ expect(['upload', 'download', 'delete', 'update', 'list']).toContain(op.operation);
393
+ if (op.fileId) {
394
+ expect(typeof op.fileId).toBe('string');
395
+ }
396
+ if (op.options) {
397
+ expect(typeof op.options).toBe('object');
398
+ }
399
+ if (op.metadata) {
400
+ expect(typeof op.metadata).toBe('object');
401
+ }
402
+ });
403
+ });
404
+ });
405
+
406
+ describe('File Validation Rules', () => {
407
+ it('validates file validation rule types', () => {
408
+ interface FileValidationRule {
409
+ field: keyof FileReference;
410
+ validator: (value: any) => boolean;
411
+ message: string;
412
+ required?: boolean;
413
+ }
414
+
415
+ const validationRules: FileValidationRule[] = [
416
+ {
417
+ field: 'filename',
418
+ validator: (value) => typeof value === 'string' && value.length > 0,
419
+ message: 'Filename is required',
420
+ required: true
421
+ },
422
+ {
423
+ field: 'size',
424
+ validator: (value) => typeof value === 'number' && value > 0,
425
+ message: 'File size must be greater than 0'
426
+ },
427
+ {
428
+ field: 'mimeType',
429
+ validator: (value) => typeof value === 'string' && value.includes('/'),
430
+ message: 'Invalid MIME type format'
431
+ }
432
+ ];
433
+
434
+ validationRules.forEach(rule => {
435
+ expect(rule).toHaveProperty('field');
436
+ expect(rule).toHaveProperty('validator');
437
+ expect(rule).toHaveProperty('message');
438
+ expect(typeof rule.field).toBe('string');
439
+ expect(typeof rule.validator).toBe('function');
440
+ expect(typeof rule.message).toBe('string');
441
+ if (rule.required !== undefined) {
442
+ expect(typeof rule.required).toBe('boolean');
443
+ }
444
+ });
445
+ });
446
+ });
447
+ });
@@ -21,7 +21,7 @@ describe('formatDate Utility', () => {
21
21
  const date = new Date('2024-01-15T10:30:00.000Z');
22
22
  const result = formatDate(date);
23
23
 
24
- expect(result).toMatch(/^[A-Za-z]{3} \d{1,2}, \d{4}$/);
24
+ expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
25
25
  });
26
26
 
27
27
  it('formats different Date objects', () => {
@@ -33,7 +33,7 @@ describe('formatDate Utility', () => {
33
33
 
34
34
  dates.forEach(date => {
35
35
  const result = formatDate(date);
36
- expect(result).toMatch(/^[A-Za-z]{3} \d{1,2}, \d{4}$/);
36
+ expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
37
37
  });
38
38
  });
39
39
  });
@@ -41,12 +41,12 @@ describe('formatDate Utility', () => {
41
41
  describe('String Input', () => {
42
42
  it('formats ISO date strings correctly', () => {
43
43
  const result = formatDate('2024-01-15T10:30:00.000Z');
44
- expect(result).toMatch(/^[A-Za-z]{3} \d{1,2}, \d{4}$/);
44
+ expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
45
45
  });
46
46
 
47
47
  it('formats date-only strings correctly', () => {
48
48
  const result = formatDate('2024-01-15');
49
- expect(result).toMatch(/^[A-Za-z]{3} \d{1,2}, \d{4}$/);
49
+ expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
50
50
  });
51
51
 
52
52
  it('formats various date string formats', () => {
@@ -59,7 +59,7 @@ describe('formatDate Utility', () => {
59
59
 
60
60
  dateStrings.forEach(dateString => {
61
61
  const result = formatDate(dateString);
62
- expect(result).toMatch(/^[A-Za-z]{3} \d{1,2}, \d{4}$/);
62
+ expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
63
63
  });
64
64
  });
65
65
  });
@@ -68,13 +68,13 @@ describe('formatDate Utility', () => {
68
68
  it('formats timestamp numbers correctly', () => {
69
69
  const timestamp = new Date('2024-01-15T10:30:00.000Z').getTime();
70
70
  const result = formatDate(timestamp);
71
- expect(result).toMatch(/^[A-Za-z]{3} \d{1,2}, \d{4}$/);
71
+ expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
72
72
  });
73
73
 
74
74
  it('formats Unix timestamps correctly', () => {
75
75
  const unixTimestamp = Math.floor(new Date('2024-01-15T10:30:00.000Z').getTime() / 1000);
76
76
  const result = formatDate(unixTimestamp * 1000); // Convert back to milliseconds
77
- expect(result).toMatch(/^[A-Za-z]{3} \d{1,2}, \d{4}$/);
77
+ expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
78
78
  });
79
79
  });
80
80
 
@@ -154,14 +154,14 @@ describe('formatDate Utility', () => {
154
154
  const result = formatDate(date);
155
155
 
156
156
  // Should use the default locale format
157
- expect(result).toMatch(/^[A-Za-z]{3} \d{1,2}, \d{4}$/);
157
+ expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
158
158
  });
159
159
 
160
160
  it('handles different locales correctly', () => {
161
161
  const date = new Date('2024-01-15T10:30:00.000Z');
162
162
 
163
163
  const result = formatDate(date);
164
- expect(result).toMatch(/^[A-Za-z]{3} \d{1,2}, \d{4}$/);
164
+ expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
165
165
  });
166
166
  });
167
167
 
@@ -171,7 +171,7 @@ describe('formatDate Utility', () => {
171
171
  const result = formatDate(date);
172
172
 
173
173
  // Should format consistently regardless of timezone
174
- expect(result).toMatch(/^[A-Za-z]{3} \d{1,2}, \d{4}$/);
174
+ expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
175
175
  });
176
176
  });
177
177
 
@@ -231,7 +231,7 @@ describe('formatDate Utility', () => {
231
231
  const result = formatDate(date);
232
232
 
233
233
  // Should match pattern: "MMM DD, YYYY"
234
- expect(result).toMatch(/^[A-Za-z]{3} \d{1,2}, \d{4}$/);
234
+ expect(result).toMatch(/^\d{1,2} [A-Za-z]{3} \d{4}$/);
235
235
  });
236
236
  });
237
237
  });
@@ -3,14 +3,15 @@
3
3
  */
4
4
 
5
5
  /**
6
- * Format a date as a readable string
6
+ * Format a date as a readable string in "dd mmm yyyy" format (e.g., "15 Jun 2024")
7
7
  */
8
8
  export function formatDate(date: Date | string | number): string {
9
9
  const dateObj = typeof date === 'string' || typeof date === 'number'
10
10
  ? new Date(date)
11
11
  : date;
12
12
 
13
- return dateObj.toLocaleDateString(undefined, {
13
+ // Use 'en-GB' locale to ensure "dd mmm yyyy" format (e.g., "15 Jun 2024")
14
+ return dateObj.toLocaleDateString('en-GB', {
14
15
  year: 'numeric',
15
16
  month: 'short',
16
17
  day: 'numeric'
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/organisationContext.ts"],"sourcesContent":["/**\n * @file Organisation Context Utility\n * @package @jmruthers/pace-core\n * @module Utils/OrganisationContext\n * @since 0.4.0\n *\n * Utility functions for managing organisation context in database sessions.\n * Provides fallback mechanisms for when database functions are not available.\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\n\n/**\n * Set organisation context in the database session\n * \n * This function attempts to set the organisation context using a database function.\n * If the function is not available, it falls back gracefully without throwing errors.\n * \n * @param supabase - Supabase client instance\n * @param organisationId - The organisation ID to set as context\n * @returns Promise that resolves when context is set (or falls back gracefully)\n */\nexport async function setOrganisationContext(\n supabase: SupabaseClient,\n organisationId: string\n): Promise<void> {\n if (!supabase || !organisationId) {\n // TODO: Replace with proper logging service integration\n return;\n }\n\n try {\n // Add timeout to prevent hanging RPC calls\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => reject(new Error('RPC timeout after 3 seconds')), 3000);\n });\n \n // Call the database function to set organisation context\n const rpcPromise = supabase.rpc('set_organisation_context', {\n org_id: organisationId\n });\n\n const { error } = await Promise.race([rpcPromise, timeoutPromise]) as any;\n\n if (error) {\n // Function might not exist yet - this is expected during migration\n // Silent fail - will fall back to client-side filtering\n console.log('[organisationContext] RPC function not available or failed, continuing without database context');\n } else {\n console.log('[organisationContext] Organisation context set in database successfully');\n }\n } catch (error) {\n // Handle any other errors gracefully\n // Silent fail - will fall back to client-side filtering\n console.log('[organisationContext] Failed to set database context, continuing without it:', error);\n }\n}\n\n/**\n * Clear organisation context from the database session\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves when context is cleared\n */\nexport async function clearOrganisationContext(\n supabase: SupabaseClient\n): Promise<void> {\n if (!supabase) {\n // TODO: Replace with proper logging service integration\n return;\n }\n\n try {\n const { error } = await supabase.rpc('rbac_audit_log', {\n p_event_type: 'organisation_switched',\n p_metadata: { action: 'clear_context' }\n });\n \n if (error) {\n // Silent fail - function not available\n // TODO: Replace with proper logging service integration\n } else {\n // TODO: Replace with proper logging service integration\n }\n } catch (error) {\n // Silent fail - error occurred\n // TODO: Replace with proper logging service integration\n }\n}\n\n/**\n * Get current organisation context from the database session\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves to the current organisation ID or null\n */\nexport async function getOrganisationContext(\n supabase: SupabaseClient\n): Promise<string | null> {\n if (!supabase) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n\n try {\n // For now, return null since we're not using database context\n // This will be replaced with proper context management\n const data = null;\n const error = null;\n \n if (error) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n \n // Validate that data is a string (allow empty strings)\n if (typeof data === 'string') {\n return data;\n }\n \n // Return null for invalid data formats\n return null;\n } catch (error) {\n // TODO: Replace with proper logging service integration\n return null;\n }\n}\n\n/**\n * Check if organisation context functions are available in the database\n * \n * @param supabase - Supabase client instance\n * @returns Promise that resolves to true if functions are available\n */\nexport async function isOrganisationContextAvailable(\n supabase: SupabaseClient\n): Promise<boolean> {\n if (!supabase) {\n return false;\n }\n\n try {\n const { error } = await supabase.rpc('get_organisation_context');\n \n if (error) {\n return false;\n }\n \n return true;\n } catch (error) {\n return false;\n }\n} "],"mappings":";;;;;AAsBA,eAAsB,uBACpB,UACA,gBACe;AACf,MAAI,CAAC,YAAY,CAAC,gBAAgB;AAEhC;AAAA,EACF;AAEA,MAAI;AAEF,UAAM,iBAAiB,IAAI,QAAQ,CAAC,GAAG,WAAW;AAChD,iBAAW,MAAM,OAAO,IAAI,MAAM,6BAA6B,CAAC,GAAG,GAAI;AAAA,IACzE,CAAC;AAGD,UAAM,aAAa,SAAS,IAAI,4BAA4B;AAAA,MAC1D,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,EAAE,MAAM,IAAI,MAAM,QAAQ,KAAK,CAAC,YAAY,cAAc,CAAC;AAEjE,QAAI,OAAO;AAGT,cAAQ,IAAI,iGAAiG;AAAA,IAC/G,OAAO;AACL,cAAQ,IAAI,yEAAyE;AAAA,IACvF;AAAA,EACF,SAAS,OAAO;AAGd,YAAQ,IAAI,gFAAgF,KAAK;AAAA,EACnG;AACF;AAQA,eAAsB,yBACpB,UACe;AACf,MAAI,CAAC,UAAU;AAEb;AAAA,EACF;AAEA,MAAI;AACF,UAAM,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,kBAAkB;AAAA,MACrD,cAAc;AAAA,MACd,YAAY,EAAE,QAAQ,gBAAgB;AAAA,IACxC,CAAC;AAED,QAAI,OAAO;AAAA,IAGX,OAAO;AAAA,IAEP;AAAA,EACF,SAAS,OAAO;AAAA,EAGhB;AACF;AAQA,eAAsB,uBACpB,UACwB;AACxB,MAAI,CAAC,UAAU;AAEb,WAAO;AAAA,EACT;AAEA,MAAI;AAGF,UAAM,OAAO;AACb,UAAM,QAAQ;AAEd,QAAI,OAAO;AAET,aAAO;AAAA,IACT;AAGA,QAAI,OAAO,SAAS,UAAU;AAC5B,aAAO;AAAA,IACT;AAGA,WAAO;AAAA,EACT,SAAS,OAAO;AAEd,WAAO;AAAA,EACT;AACF;AAQA,eAAsB,+BACpB,UACkB;AAClB,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,0BAA0B;AAE/D,QAAI,OAAO;AACT,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO;AAAA,EACT;AACF;AAxJA;AAAA;AAAA;AAAA;AAAA;","names":[]}