@jmruthers/pace-core 0.5.75 → 0.5.77

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 (507) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{RBACService-C4udt_Zp.d.ts → AuthService-SBHZQtCH.d.ts} +5 -118
  3. package/dist/{DataTable-ntgmhO2W.d.ts → DataTable-BE0OXZKQ.d.ts} +9 -2
  4. package/dist/DataTable-QCNCV6IK.js +157 -0
  5. package/dist/{PublicLoadingSpinner-BKNBT6b6.d.ts → PublicLoadingSpinner-CnUaz0vG.d.ts} +33 -19
  6. package/dist/{UnifiedAuthProvider-Bj6YCf7c.d.ts → UnifiedAuthProvider-B391Aqum.d.ts} +42 -45
  7. package/dist/{UnifiedAuthProvider-3NKDOSOK.js → UnifiedAuthProvider-Z2FWNW7O.js} +4 -5
  8. package/dist/{api-DDMUKIUD.js → api-KG4A2X7P.js} +9 -3
  9. package/dist/{audit-6TOCAMKO.js → audit-65VNHEV2.js} +2 -2
  10. package/dist/{chunk-2DFZ432F.js → chunk-7PX43UYN.js} +197 -629
  11. package/dist/chunk-7PX43UYN.js.map +1 -0
  12. package/dist/{chunk-DAXLNIDY.js → chunk-C4RQ3GQA.js} +108 -32
  13. package/dist/chunk-C4RQ3GQA.js.map +1 -0
  14. package/dist/{chunk-LW7MMEAQ.js → chunk-CRKP3HXI.js} +2 -2
  15. package/dist/{chunk-XLZ7U46Z.js → chunk-CVMVPYAL.js} +9 -60
  16. package/dist/chunk-CVMVPYAL.js.map +1 -0
  17. package/dist/{chunk-CY3AHGO4.js → chunk-DDPG7FCX.js} +3395 -3254
  18. package/dist/chunk-DDPG7FCX.js.map +1 -0
  19. package/dist/{chunk-URUTVZ7N.js → chunk-DVHZ5L55.js} +2 -2
  20. package/dist/{chunk-5BSLGBYI.js → chunk-JCQZ6LA7.js} +2 -8
  21. package/dist/{chunk-5BSLGBYI.js.map → chunk-JCQZ6LA7.js.map} +1 -1
  22. package/dist/{chunk-WN6XJWOS.js → chunk-JDQ7T3QB.js} +256 -743
  23. package/dist/chunk-JDQ7T3QB.js.map +1 -0
  24. package/dist/{chunk-ZTT2AXMX.js → chunk-LMYTEMUH.js} +153 -132
  25. package/dist/chunk-LMYTEMUH.js.map +1 -0
  26. package/dist/{chunk-33PHABLB.js → chunk-NKT2DLZI.js} +13 -130
  27. package/dist/chunk-NKT2DLZI.js.map +1 -0
  28. package/dist/chunk-PUKTJMRT.js +732 -0
  29. package/dist/chunk-PUKTJMRT.js.map +1 -0
  30. package/dist/{chunk-B2WTCLCV.js → chunk-Q7APDV6H.js} +18 -8
  31. package/dist/chunk-Q7APDV6H.js.map +1 -0
  32. package/dist/{chunk-FGMFQSHX.js → chunk-S63MFSY6.js} +500 -551
  33. package/dist/chunk-S63MFSY6.js.map +1 -0
  34. package/dist/{chunk-NTNILOBC.js → chunk-TLD5BEU6.js} +4 -4
  35. package/dist/chunk-WUXCWRL6.js +20 -0
  36. package/dist/chunk-WUXCWRL6.js.map +1 -0
  37. package/dist/{chunk-YNUBMSMV.js → chunk-YCKPEMJA.js} +186 -263
  38. package/dist/chunk-YCKPEMJA.js.map +1 -0
  39. package/dist/{chunk-A4FUBC7B.js → chunk-Z3T6RK3K.js} +2 -4
  40. package/dist/{chunk-A4FUBC7B.js.map → chunk-Z3T6RK3K.js.map} +1 -1
  41. package/dist/components.d.ts +6 -6
  42. package/dist/components.js +17 -20
  43. package/dist/components.js.map +1 -1
  44. package/dist/{database-C3Szpi5J.d.ts → database-BXAfr2Y_.d.ts} +18 -0
  45. package/dist/hooks.d.ts +21 -44
  46. package/dist/hooks.js +12 -13
  47. package/dist/hooks.js.map +1 -1
  48. package/dist/index.d.ts +19 -27
  49. package/dist/index.js +27 -33
  50. package/dist/index.js.map +1 -1
  51. package/dist/{organisation-BtshODVF.d.ts → organisation-D6qRDtbF.d.ts} +1 -1
  52. package/dist/providers.d.ts +7 -21
  53. package/dist/providers.js +3 -10
  54. package/dist/rbac/index.d.ts +118 -215
  55. package/dist/rbac/index.js +18 -18
  56. package/dist/{types-CGX9Vyf5.d.ts → types-BDg1mAGG.d.ts} +36 -6
  57. package/dist/types.d.ts +3 -3
  58. package/dist/types.js +61 -18
  59. package/dist/types.js.map +1 -1
  60. package/dist/{unified-CM7T0aTK.d.ts → unified-DQ4VcT7H.d.ts} +1 -1
  61. package/dist/{usePublicRouteParams-B-CumWRc.d.ts → usePublicRouteParams-BlgwXweB.d.ts} +3 -3
  62. package/dist/utils.d.ts +2 -2
  63. package/dist/utils.js +52 -9
  64. package/dist/utils.js.map +1 -1
  65. package/docs/CONTENT_AUDIT_REPORT.md +253 -0
  66. package/docs/DOCUMENTATION_AUDIT.md +172 -0
  67. package/docs/README.md +142 -147
  68. package/docs/STYLE_GUIDE.md +37 -0
  69. package/docs/api/classes/ColumnFactory.md +17 -17
  70. package/docs/api/classes/ErrorBoundary.md +1 -1
  71. package/docs/api/classes/InvalidScopeError.md +4 -4
  72. package/docs/api/classes/MissingUserContextError.md +4 -4
  73. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  74. package/docs/api/classes/PermissionDeniedError.md +5 -5
  75. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  76. package/docs/api/classes/RBACAuditManager.md +8 -8
  77. package/docs/api/classes/RBACCache.md +35 -5
  78. package/docs/api/classes/RBACEngine.md +49 -20
  79. package/docs/api/classes/RBACError.md +4 -4
  80. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  81. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  82. package/docs/api/classes/StorageUtils.md +1 -1
  83. package/docs/api/enums/FileCategory.md +1 -1
  84. package/docs/api/interfaces/AggregateConfig.md +4 -4
  85. package/docs/api/interfaces/ButtonProps.md +1 -1
  86. package/docs/api/interfaces/CardProps.md +1 -1
  87. package/docs/api/interfaces/ColorPalette.md +1 -1
  88. package/docs/api/interfaces/ColorShade.md +1 -1
  89. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  90. package/docs/api/interfaces/DataRecord.md +11 -0
  91. package/docs/api/interfaces/DataTableAction.md +65 -29
  92. package/docs/api/interfaces/DataTableColumn.md +36 -23
  93. package/docs/api/interfaces/DataTableProps.md +80 -38
  94. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  95. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  96. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  97. package/docs/api/interfaces/EventLogoProps.md +1 -1
  98. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  99. package/docs/api/interfaces/FileMetadata.md +1 -1
  100. package/docs/api/interfaces/FileReference.md +1 -1
  101. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  102. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  103. package/docs/api/interfaces/FileUploadProps.md +1 -1
  104. package/docs/api/interfaces/FooterProps.md +1 -1
  105. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  106. package/docs/api/interfaces/InputProps.md +1 -1
  107. package/docs/api/interfaces/LabelProps.md +1 -1
  108. package/docs/api/interfaces/LoginFormProps.md +1 -1
  109. package/docs/api/interfaces/NavigationAccessRecord.md +11 -11
  110. package/docs/api/interfaces/NavigationContextType.md +9 -9
  111. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  112. package/docs/api/interfaces/NavigationItem.md +1 -1
  113. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  114. package/docs/api/interfaces/NavigationProviderProps.md +7 -7
  115. package/docs/api/interfaces/Organisation.md +1 -1
  116. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  117. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  118. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  119. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  120. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  121. package/docs/api/interfaces/PaceLoginPageProps.md +16 -3
  122. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  123. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  124. package/docs/api/interfaces/PagePermissionGuardProps.md +2 -2
  125. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  126. package/docs/api/interfaces/PaletteData.md +1 -1
  127. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  128. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  129. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  130. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  131. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  132. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  133. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  134. package/docs/api/interfaces/RBACConfig.md +1 -1
  135. package/docs/api/interfaces/RBACLogger.md +1 -1
  136. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  137. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  138. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  139. package/docs/api/interfaces/RouteConfig.md +2 -2
  140. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  141. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  142. package/docs/api/interfaces/StorageConfig.md +1 -1
  143. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  144. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  145. package/docs/api/interfaces/StorageListOptions.md +1 -1
  146. package/docs/api/interfaces/StorageListResult.md +1 -1
  147. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  148. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  149. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  150. package/docs/api/interfaces/StyleImport.md +1 -1
  151. package/docs/api/interfaces/SwitchProps.md +1 -1
  152. package/docs/api/interfaces/ToastActionElement.md +1 -1
  153. package/docs/api/interfaces/ToastProps.md +1 -1
  154. package/docs/api/interfaces/UnifiedAuthContextType.md +94 -521
  155. package/docs/api/interfaces/UnifiedAuthProviderProps.md +16 -16
  156. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  157. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  158. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  159. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  160. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  161. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  162. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  163. package/docs/api/interfaces/UseResolvedScopeOptions.md +47 -0
  164. package/docs/api/interfaces/UseResolvedScopeReturn.md +47 -0
  165. package/docs/api/interfaces/UserEventAccess.md +11 -11
  166. package/docs/api/interfaces/UserMenuProps.md +1 -1
  167. package/docs/api/interfaces/UserProfile.md +1 -1
  168. package/docs/api/modules.md +303 -275
  169. package/docs/api-reference/components.md +193 -0
  170. package/docs/api-reference/hooks.md +265 -0
  171. package/docs/api-reference/providers.md +32 -7
  172. package/docs/api-reference/types.md +6 -0
  173. package/docs/api-reference/utilities.md +207 -0
  174. package/docs/architecture/README.md +6 -0
  175. package/docs/{database-schema-requirements.md → architecture/database-schema-requirements.md} +6 -0
  176. package/docs/architecture/rbac-security-architecture.md +258 -0
  177. package/docs/architecture/services.md +9 -1
  178. package/docs/best-practices/README.md +26 -0
  179. package/docs/best-practices/accessibility.md +572 -0
  180. package/docs/{common-patterns.md → best-practices/common-patterns.md} +6 -0
  181. package/docs/best-practices/deployment.md +6 -0
  182. package/docs/best-practices/performance.md +475 -2
  183. package/docs/best-practices/security.md +6 -0
  184. package/docs/best-practices/testing.md +6 -0
  185. package/docs/core-concepts/authentication.md +21 -7
  186. package/docs/core-concepts/events.md +6 -0
  187. package/docs/core-concepts/organisations.md +6 -0
  188. package/docs/core-concepts/permissions.md +6 -0
  189. package/docs/core-concepts/rbac-system.md +6 -0
  190. package/docs/documentation-index.md +121 -182
  191. package/docs/{consuming-app-vite-config.md → getting-started/consuming-app-vite-config.md} +6 -0
  192. package/docs/getting-started/documentation-index.md +40 -0
  193. package/docs/getting-started/examples/README.md +878 -35
  194. package/docs/{faq.md → getting-started/faq.md} +7 -1
  195. package/docs/getting-started/installation-guide.md +6 -0
  196. package/docs/{quick-reference.md → getting-started/quick-reference.md} +6 -0
  197. package/docs/implementation-guides/app-layout.md +6 -0
  198. package/docs/implementation-guides/authentication.md +1021 -0
  199. package/docs/implementation-guides/component-styling.md +416 -0
  200. package/docs/implementation-guides/data-tables.md +1264 -2076
  201. package/docs/implementation-guides/dynamic-colors.md +6 -0
  202. package/docs/implementation-guides/event-theming-summary.md +6 -0
  203. package/docs/{file-reference-system.md → implementation-guides/file-reference-system.md} +6 -0
  204. package/docs/implementation-guides/file-upload-storage.md +6 -0
  205. package/docs/implementation-guides/forms.md +6 -0
  206. package/docs/implementation-guides/inactivity-tracking.md +6 -0
  207. package/docs/implementation-guides/navigation.md +6 -0
  208. package/docs/implementation-guides/organisation-security.md +6 -0
  209. package/docs/implementation-guides/permission-enforcement.md +6 -0
  210. package/docs/implementation-guides/public-pages-advanced.md +6 -0
  211. package/docs/implementation-guides/public-pages.md +6 -0
  212. package/docs/migration/MIGRATION_GUIDE.md +827 -351
  213. package/docs/migration/README.md +7 -1
  214. package/docs/migration/organisation-context-timing-fix.md +6 -0
  215. package/docs/migration/rbac-migration.md +44 -1
  216. package/docs/migration/service-architecture.md +6 -0
  217. package/docs/migration/v0.4.15-tailwind-scanning.md +6 -0
  218. package/docs/migration/v0.4.16-css-first-approach.md +6 -0
  219. package/docs/migration/v0.4.17-source-path-fix.md +6 -0
  220. package/docs/rbac/README-rbac-rls-integration.md +6 -0
  221. package/docs/rbac/README.md +6 -0
  222. package/docs/rbac/advanced-patterns.md +6 -0
  223. package/docs/rbac/api-reference.md +7 -1
  224. package/docs/rbac/breaking-changes-v3.md +222 -0
  225. package/docs/rbac/examples/rbac-rls-integration-example.md +6 -0
  226. package/docs/rbac/examples.md +6 -0
  227. package/docs/rbac/getting-started.md +6 -0
  228. package/docs/rbac/migration-guide.md +260 -0
  229. package/docs/rbac/quick-start.md +6 -0
  230. package/docs/rbac/rbac-rls-integration.md +6 -0
  231. package/docs/rbac/super-admin-guide.md +6 -0
  232. package/docs/rbac/troubleshooting.md +6 -0
  233. package/docs/security/README.md +6 -0
  234. package/docs/security/checklist.md +6 -0
  235. package/docs/styles/README.md +7 -1
  236. package/docs/{usage.md → styles/usage.md} +6 -0
  237. package/docs/testing/README.md +6 -0
  238. package/docs/{visual-testing.md → testing/visual-testing.md} +6 -0
  239. package/docs/troubleshooting/README.md +387 -5
  240. package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +6 -0
  241. package/docs/troubleshooting/common-issues.md +6 -0
  242. package/docs/troubleshooting/database-view-compatibility.md +6 -0
  243. package/docs/troubleshooting/organisation-context-setup.md +6 -0
  244. package/docs/troubleshooting/react-hooks-issue-analysis.md +6 -0
  245. package/docs/troubleshooting/styling-issues.md +6 -0
  246. package/docs/troubleshooting/tailwind-content-scanning.md +6 -0
  247. package/package.json +1 -1
  248. package/src/__tests__/TEST_GUIDE_CURSOR.md +290 -0
  249. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -1
  250. package/src/__tests__/helpers/supabaseMock.ts +48 -2
  251. package/src/__tests__/helpers/test-providers.tsx +3 -53
  252. package/src/components/DataTable/DataTable.test.tsx +319 -0
  253. package/src/components/DataTable/DataTable.tsx +32 -11
  254. package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx → DataTable.comprehensive.test.tsx.skip} +6 -4
  255. package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +17 -6
  256. package/src/components/DataTable/__tests__/{DataTable.test.tsx → DataTable.test.tsx.skip} +6 -4
  257. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +96 -10
  258. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +601 -0
  259. package/src/components/DataTable/__tests__/keyboard.test.tsx +615 -0
  260. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +639 -0
  261. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx.skip +330 -0
  262. package/src/components/DataTable/components/AccessDeniedPage.tsx +2 -2
  263. package/src/components/DataTable/components/ActionButtons.tsx +88 -104
  264. package/src/components/DataTable/components/DataTableCore.tsx +442 -665
  265. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +4 -2
  266. package/src/components/DataTable/components/DataTableModals.tsx +22 -1
  267. package/src/components/DataTable/components/EditableRow.tsx +69 -84
  268. package/src/components/DataTable/components/EmptyState.tsx +5 -1
  269. package/src/components/DataTable/components/ImportModal.tsx +65 -36
  270. package/src/components/DataTable/components/PaginationControls.tsx +40 -100
  271. package/src/components/DataTable/components/UnifiedTableBody.tsx +222 -278
  272. package/src/components/DataTable/components/index.ts +1 -2
  273. package/src/components/DataTable/context/DataTableContext.tsx +1 -1
  274. package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +208 -275
  275. package/src/components/DataTable/core/ColumnFactory.ts +5 -0
  276. package/src/components/DataTable/core/index.ts +1 -8
  277. package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +12 -10
  278. package/src/components/DataTable/examples/HierarchicalExample.tsx +1 -1
  279. package/src/components/DataTable/examples/InitialPageSizeExample.tsx +1 -0
  280. package/src/components/DataTable/examples/PerformanceExample.tsx +1 -0
  281. package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +521 -0
  282. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +570 -0
  283. package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +167 -0
  284. package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +214 -0
  285. package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +224 -0
  286. package/src/components/DataTable/hooks/index.ts +13 -0
  287. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +32 -15
  288. package/src/components/DataTable/hooks/useColumnReordering.ts +1 -0
  289. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +102 -0
  290. package/src/components/DataTable/hooks/useDataTableConfiguration.ts +89 -0
  291. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +117 -0
  292. package/src/components/DataTable/hooks/useDataTablePermissions.ts +193 -0
  293. package/src/components/DataTable/hooks/useDataTableState.ts +51 -17
  294. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +33 -0
  295. package/src/components/DataTable/hooks/useHierarchicalState.ts +41 -9
  296. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +447 -0
  297. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +94 -0
  298. package/src/components/DataTable/hooks/useTableColumns.ts +156 -0
  299. package/src/components/DataTable/hooks/useTableHandlers.ts +174 -0
  300. package/src/components/DataTable/index.ts +13 -12
  301. package/src/components/DataTable/types.ts +129 -9
  302. package/src/components/DataTable/utils/__tests__/COVERAGE_NOTE.md +89 -0
  303. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +162 -28
  304. package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +573 -0
  305. package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +247 -0
  306. package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +8 -6
  307. package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +466 -0
  308. package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +251 -0
  309. package/src/components/DataTable/utils/a11yUtils.ts +244 -0
  310. package/src/components/DataTable/utils/debugTools.ts +47 -21
  311. package/src/components/DataTable/utils/errorHandling.ts +52 -460
  312. package/src/components/DataTable/utils/exportUtils.ts +157 -28
  313. package/src/components/DataTable/utils/flexibleImport.ts +202 -32
  314. package/src/components/DataTable/utils/hierarchicalSorting.ts +50 -3
  315. package/src/components/DataTable/utils/hierarchicalUtils.ts +167 -34
  316. package/src/components/DataTable/utils/index.ts +7 -0
  317. package/src/components/DataTable/utils/paginationUtils.ts +350 -0
  318. package/src/components/DataTable/utils/rowUtils.ts +69 -0
  319. package/src/components/EventSelector/EventSelector.test.tsx +672 -0
  320. package/src/components/Label/__tests__/Label.test.tsx +434 -0
  321. package/src/components/NavigationMenu/NavigationMenu.test.tsx +19 -24
  322. package/src/components/NavigationMenu/NavigationMenu.tsx +19 -8
  323. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +1 -23
  324. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +56 -6
  325. package/src/components/PaceLoginPage/PaceLoginPage.tsx +137 -13
  326. package/src/components/PublicLayout/__tests__/PublicPageContextChecker.test.tsx +190 -0
  327. package/src/components/PublicLayout/__tests__/PublicPageDebugger.test.tsx +185 -0
  328. package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +1 -1
  329. package/src/components/PublicLayout/__tests__/PublicPageProvider.test.tsx +313 -0
  330. package/src/components/Select/Select.test.tsx +143 -120
  331. package/src/components/Select/Select.tsx +48 -212
  332. package/src/components/Select/hooks.ts +36 -1
  333. package/src/components/Select/index.ts +2 -1
  334. package/src/components/examples/PermissionExample.tsx +173 -0
  335. package/src/examples/CorrectPublicPageImplementation.tsx +301 -0
  336. package/src/examples/PublicEventPage.tsx +274 -0
  337. package/src/examples/PublicPageApp.tsx +308 -0
  338. package/src/examples/PublicPageUsageExample.tsx +216 -0
  339. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +12 -1
  340. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +129 -17
  341. package/src/hooks/__tests__/useRBAC.unit.test.ts +151 -846
  342. package/src/hooks/useOrganisationPermissions.test.ts +42 -18
  343. package/src/hooks/useOrganisationPermissions.ts +12 -6
  344. package/src/hooks/useOrganisationSecurity.test.ts +138 -85
  345. package/src/hooks/useOrganisationSecurity.ts +41 -10
  346. package/src/hooks/useSecureDataAccess.test.ts +32 -29
  347. package/src/index.ts +0 -1
  348. package/src/providers/AuthProvider.simplified.tsx +880 -0
  349. package/src/providers/UnifiedAuthProvider.test.simple.tsx +8 -8
  350. package/src/providers/__tests__/ProviderLifecycle.test.tsx +341 -0
  351. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +29 -19
  352. package/src/providers/index.ts +0 -1
  353. package/src/providers/services/EventServiceProvider.tsx +19 -15
  354. package/src/providers/services/InactivityServiceProvider.tsx +19 -15
  355. package/src/providers/services/OrganisationServiceProvider.tsx +19 -15
  356. package/src/providers/services/UnifiedAuthProvider.tsx +156 -127
  357. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +1 -1
  358. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -3
  359. package/src/rbac/README.md +1 -1
  360. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +25 -27
  361. package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +313 -0
  362. package/src/rbac/__tests__/engine.comprehensive.test.ts +114 -348
  363. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +28 -110
  364. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +33 -85
  365. package/src/rbac/__tests__/scenarios.user-role.test.tsx +2 -2
  366. package/src/rbac/adapters.tsx +26 -69
  367. package/src/rbac/api.test.ts +90 -27
  368. package/src/rbac/api.ts +61 -10
  369. package/src/rbac/audit.test.ts +33 -38
  370. package/src/rbac/audit.ts +21 -6
  371. package/src/rbac/cache.ts +33 -1
  372. package/src/rbac/components/NavigationGuard.tsx +11 -11
  373. package/src/rbac/components/NavigationProvider.test.tsx +11 -5
  374. package/src/rbac/components/NavigationProvider.tsx +37 -13
  375. package/src/rbac/components/PagePermissionGuard.tsx +111 -50
  376. package/src/rbac/components/PagePermissionProvider.tsx +5 -5
  377. package/src/rbac/components/PermissionEnforcer.tsx +11 -11
  378. package/src/rbac/components/RoleBasedRouter.tsx +5 -5
  379. package/src/rbac/components/SecureDataProvider.tsx +5 -5
  380. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +8 -8
  381. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +14 -14
  382. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +12 -12
  383. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +6 -6
  384. package/src/rbac/engine.test.simple.ts +19 -13
  385. package/src/rbac/engine.test.ts +1 -0
  386. package/src/rbac/engine.ts +330 -766
  387. package/src/rbac/errors.ts +156 -0
  388. package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +437 -0
  389. package/src/rbac/hooks/index.ts +2 -0
  390. package/src/rbac/hooks/usePermissions.ts +32 -10
  391. package/src/rbac/hooks/useRBAC.test.ts +126 -512
  392. package/src/rbac/hooks/useRBAC.ts +147 -193
  393. package/src/rbac/hooks/useResolvedScope.ts +244 -0
  394. package/src/rbac/index.ts +7 -4
  395. package/src/rbac/security.ts +109 -18
  396. package/src/rbac/types.ts +12 -1
  397. package/src/services/AuthService.ts +2 -15
  398. package/src/services/EventService.ts +26 -46
  399. package/src/services/OrganisationService.ts +51 -31
  400. package/src/services/__tests__/AuthService.test.ts +1 -1
  401. package/src/services/__tests__/EventService.test.ts +1 -1
  402. package/src/services/__tests__/InactivityService.lifecycle.test.ts +411 -0
  403. package/src/services/__tests__/OrganisationService.pagination.test.ts +375 -0
  404. package/src/services/__tests__/OrganisationService.test.ts +1 -1
  405. package/src/styles/base.css +208 -0
  406. package/src/styles/semantic.css +24 -0
  407. package/src/types/__tests__/README.md +114 -0
  408. package/src/types/__tests__/validation.test.ts +731 -0
  409. package/src/types/database.generated.ts +7347 -0
  410. package/src/types/database.ts +20 -0
  411. package/src/utils/__tests__/file-reference.test.ts +383 -0
  412. package/src/utils/__tests__/performanceBenchmark.test.ts +175 -0
  413. package/src/utils/appNameResolver.test.ts +54 -0
  414. package/src/utils/logger.ts +179 -0
  415. package/src/utils/organisationContext.ts +11 -4
  416. package/src/utils/storage/__tests__/helpers.unit.test.ts +6 -2
  417. package/src/validation/__tests__/csrf.unit.test.ts +63 -0
  418. package/src/validation/__tests__/passwordSchema.unit.test.ts +105 -0
  419. package/dist/DataTable-HWZQGASI.js +0 -102
  420. package/dist/appNameResolver-UURKN7NF.js +0 -22
  421. package/dist/audit-6TOCAMKO.js.map +0 -1
  422. package/dist/chunk-2CHATWBF.js +0 -523
  423. package/dist/chunk-2CHATWBF.js.map +0 -1
  424. package/dist/chunk-2DFZ432F.js.map +0 -1
  425. package/dist/chunk-33PHABLB.js.map +0 -1
  426. package/dist/chunk-B2WTCLCV.js.map +0 -1
  427. package/dist/chunk-CY3AHGO4.js.map +0 -1
  428. package/dist/chunk-DAXLNIDY.js.map +0 -1
  429. package/dist/chunk-FGMFQSHX.js.map +0 -1
  430. package/dist/chunk-TYHR5X4W.js +0 -33
  431. package/dist/chunk-TYHR5X4W.js.map +0 -1
  432. package/dist/chunk-ULBI5JGB.js +0 -109
  433. package/dist/chunk-ULBI5JGB.js.map +0 -1
  434. package/dist/chunk-WN6XJWOS.js.map +0 -1
  435. package/dist/chunk-XLZ7U46Z.js.map +0 -1
  436. package/dist/chunk-YNUBMSMV.js.map +0 -1
  437. package/dist/chunk-ZTT2AXMX.js.map +0 -1
  438. package/dist/eventContext-BBA42P6G.js +0 -14
  439. package/dist/eventContext-BBA42P6G.js.map +0 -1
  440. package/docs/DOCUMENTATION_CHECKLIST.md +0 -281
  441. package/docs/api/interfaces/RBACContextType.md +0 -468
  442. package/docs/api/interfaces/RBACProviderProps.md +0 -107
  443. package/docs/breaking-changes.md +0 -179
  444. package/docs/consuming-app-example.md +0 -290
  445. package/docs/documentation-style-checklist.md +0 -294
  446. package/docs/examples/navigation-menu-auth-fix.md +0 -344
  447. package/docs/getting-started/examples/basic-auth-app.md +0 -520
  448. package/docs/getting-started/examples/full-featured-app.md +0 -616
  449. package/docs/getting-started/quick-start.md +0 -426
  450. package/docs/implementation-guides/datatable-filtering.md +0 -313
  451. package/docs/implementation-guides/datatable-rbac-usage.md +0 -317
  452. package/docs/implementation-guides/hierarchical-datatable.md +0 -850
  453. package/docs/implementation-guides/large-datasets.md +0 -281
  454. package/docs/implementation-guides/performance.md +0 -403
  455. package/docs/migration/quick-migration-guide.md +0 -320
  456. package/docs/migration-guide.md +0 -193
  457. package/docs/migration-guides/unified-auth-provider-mandatory-timeouts.md +0 -226
  458. package/docs/performance/README.md +0 -551
  459. package/docs/style-guide.md +0 -925
  460. package/docs/troubleshooting/authentication-issues.md +0 -334
  461. package/docs/troubleshooting/debugging.md +0 -1117
  462. package/docs/troubleshooting/migration.md +0 -918
  463. package/src/__tests__/hooks/usePermissions.test.ts +0 -261
  464. package/src/components/DataTable/components/DataTableBody.tsx +0 -488
  465. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -144
  466. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -515
  467. package/src/components/DataTable/core/ActionManager.ts +0 -235
  468. package/src/components/DataTable/core/ColumnManager.ts +0 -215
  469. package/src/components/DataTable/core/DataManager.ts +0 -188
  470. package/src/components/DataTable/core/DataTableContext.tsx +0 -181
  471. package/src/components/DataTable/core/LocalDataAdapter.ts +0 -264
  472. package/src/components/DataTable/core/PluginRegistry.ts +0 -229
  473. package/src/components/DataTable/core/StateManager.ts +0 -311
  474. package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -634
  475. package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -193
  476. package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -519
  477. package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -714
  478. package/src/components/DataTable/core/interfaces.ts +0 -338
  479. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +0 -574
  480. package/src/components/Select/Select.bug-test.tsx +0 -69
  481. package/src/components/Select/Select.refactored.tsx +0 -497
  482. package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -613
  483. package/src/hooks/services/usePermissions.ts +0 -70
  484. package/src/hooks/services/useRBACService.ts +0 -30
  485. package/src/hooks/usePermissionCheck.ts +0 -150
  486. package/src/providers/__tests__/ServiceProviders.test.tsx +0 -477
  487. package/src/providers/services/RBACServiceProvider.tsx +0 -79
  488. package/src/rbac/__tests__/integration.authflow.test.tsx +0 -119
  489. package/src/rbac/__tests__/integration.navigation.test.tsx +0 -69
  490. package/src/rbac/__tests__/integration.securedata.test.tsx +0 -92
  491. package/src/rbac/__tests__/integration.smoke.test.tsx +0 -73
  492. package/src/rbac/providers/RBACProvider.tsx +0 -645
  493. package/src/rbac/providers/__tests__/RBACProvider.integration.test.tsx +0 -688
  494. package/src/rbac/providers/__tests__/RBACProvider.test.tsx +0 -1186
  495. package/src/rbac/providers/index.ts +0 -11
  496. package/src/services/RBACService.ts +0 -522
  497. package/src/services/__tests__/RBACService.test.ts +0 -492
  498. package/src/services/interfaces/IRBACService.ts +0 -62
  499. package/src/utils/appNameResolver.test 2.ts +0 -494
  500. /package/dist/{DataTable-HWZQGASI.js.map → DataTable-QCNCV6IK.js.map} +0 -0
  501. /package/dist/{UnifiedAuthProvider-3NKDOSOK.js.map → UnifiedAuthProvider-Z2FWNW7O.js.map} +0 -0
  502. /package/dist/{api-DDMUKIUD.js.map → api-KG4A2X7P.js.map} +0 -0
  503. /package/dist/{appNameResolver-UURKN7NF.js.map → audit-65VNHEV2.js.map} +0 -0
  504. /package/dist/{chunk-LW7MMEAQ.js.map → chunk-CRKP3HXI.js.map} +0 -0
  505. /package/dist/{chunk-URUTVZ7N.js.map → chunk-DVHZ5L55.js.map} +0 -0
  506. /package/dist/{chunk-NTNILOBC.js.map → chunk-TLD5BEU6.js.map} +0 -0
  507. /package/docs/{app.css.example → styles/app.css.example} +0 -0
@@ -0,0 +1,573 @@
1
+ /**
2
+ * @file Unit Tests for flexibleImport Utility
3
+ * @package @jmruthers/pace-core
4
+ * @module Components/DataTable/Utils/__tests__
5
+ */
6
+
7
+ import { describe, it, expect, beforeEach } from 'vitest';
8
+ import { flexibleImport, DATE_FORMAT_SETS, commonMappings, suggestDateFormats } from '../flexibleImport';
9
+ import type { ImportOptions } from '../flexibleImport';
10
+
11
+ describe('[unit] flexibleImport', () => {
12
+ const mockCSVData = [
13
+ { 'Meal code': 'PTRY', 'Meal type': 'Pantry', 'Meal date': '26/9/2025' },
14
+ { 'Meal code': '26D', 'Meal type': 'Dinner', 'Meal date': '27/9/2025' }
15
+ ];
16
+
17
+ const mockTargetFields = ['meal_code', 'mealtype_name', 'meal_date'];
18
+
19
+ beforeEach(() => {
20
+ // Suppress console.log during tests
21
+ const originalLog = console.log;
22
+ console.log = () => {};
23
+ return () => {
24
+ console.log = originalLog;
25
+ };
26
+ });
27
+
28
+ describe('Column Mapping', () => {
29
+ it('maps CSV columns to target fields automatically', () => {
30
+ const result = flexibleImport(mockCSVData, mockTargetFields);
31
+
32
+ expect(result.mappedData).toHaveLength(2);
33
+ expect(result.mappedData[0]).toHaveProperty('meal_code');
34
+ expect(result.mappedData[0]).toHaveProperty('mealtype_name');
35
+ expect(result.mappedData[0]).toHaveProperty('meal_date');
36
+ });
37
+
38
+ it('handles case-insensitive column matching', () => {
39
+ const csvData = [
40
+ { 'meal CODE': 'PTRY', 'Meal Type': 'Pantry' }
41
+ ];
42
+
43
+ const result = flexibleImport(csvData, ['meal_code', 'mealtype_name'], {
44
+ columnMappings: {
45
+ meal_code: ['Meal CODE', 'meal code'],
46
+ mealtype_name: ['Meal Type', 'meal type']
47
+ }
48
+ });
49
+
50
+ expect(result.mappedData[0].meal_code).toBe('PTRY');
51
+ expect(result.mappedData[0].mealtype_name).toBe('Pantry');
52
+ });
53
+
54
+ it('uses exact match when available', () => {
55
+ const csvData = [
56
+ { 'meal_code': 'PTRY', 'Meal code': 'OTHER' }
57
+ ];
58
+
59
+ const result = flexibleImport(csvData, ['meal_code'], {
60
+ columnMappings: {
61
+ meal_code: ['meal_code'] // Exact match should take priority
62
+ }
63
+ });
64
+
65
+ expect(result.mappedData[0].meal_code).toBe('PTRY');
66
+ });
67
+
68
+ it('handles partial matches', () => {
69
+ const csvData = [
70
+ { 'User Meal Type': 'Pantry' }
71
+ ];
72
+
73
+ const result = flexibleImport(csvData, ['mealtype_name'], {
74
+ columnMappings: {
75
+ mealtype_name: ['meal type'] // Partial match
76
+ }
77
+ });
78
+
79
+ expect(result.mappedData[0].mealtype_name).toBe('Pantry');
80
+ });
81
+
82
+ it('uses default mappings when no custom mapping provided', () => {
83
+ const csvData = [
84
+ { 'name': 'Test User', 'email': 'test@example.com' }
85
+ ];
86
+
87
+ const result = flexibleImport(csvData, ['name', 'email']);
88
+
89
+ expect(result.mappedData[0].name).toBe('Test User');
90
+ expect(result.mappedData[0].email).toBe('test@example.com');
91
+ });
92
+
93
+ it('returns mapping information', () => {
94
+ const result = flexibleImport(mockCSVData, mockTargetFields);
95
+
96
+ expect(result.mappings).toBeDefined();
97
+ expect(result.mappings).toHaveProperty('meal_code');
98
+ expect(result.mappings).toHaveProperty('mealtype_name');
99
+ expect(result.mappings).toHaveProperty('meal_date');
100
+ });
101
+
102
+ it('handles missing columns gracefully', () => {
103
+ const csvData = [
104
+ { 'meal_code': 'PTRY' } // Missing 'mealtype_name' and 'meal_date'
105
+ ];
106
+
107
+ const result = flexibleImport(csvData, mockTargetFields);
108
+
109
+ expect(result.mappedData[0].meal_code).toBe('PTRY');
110
+ expect(result.mappedData[0].mealtype_name).toBe('');
111
+ expect(result.mappedData[0].meal_date).toBe('');
112
+ });
113
+
114
+ it('applies default values for missing fields', () => {
115
+ const csvData = [{ 'Meal code': 'PTRY' }]; // Missing fields
116
+ const result = flexibleImport(csvData, mockTargetFields, {
117
+ defaultValues: {
118
+ mealtype_name: 'Default Type',
119
+ meal_date: '2025-01-01'
120
+ }
121
+ });
122
+
123
+ expect(result.mappedData[0].meal_code).toBeDefined();
124
+ expect(result.mappedData[0].mealtype_name).toBe('Default Type');
125
+ expect(result.mappedData[0].meal_date).toBe('2025-01-01');
126
+ });
127
+ });
128
+
129
+ describe('Date Parsing', () => {
130
+ it('parses US date format (MM/DD/YYYY)', () => {
131
+ const csvData = [{ 'date': '12/31/2025' }];
132
+ const result = flexibleImport(csvData, ['date']);
133
+
134
+ const parsedDate = new Date(result.mappedData[0].date);
135
+ expect(parsedDate.getFullYear()).toBe(2025);
136
+ expect(parsedDate.getMonth()).toBe(11); // December (0-indexed)
137
+ expect(parsedDate.getDate()).toBe(31);
138
+ });
139
+
140
+ it('parses European date format (DD/MM/YYYY)', () => {
141
+ const csvData = [{ 'date': '31/12/2025' }];
142
+ const result = flexibleImport(csvData, ['date'], {
143
+ columnMappings: { date: ['date'] }
144
+ });
145
+
146
+ // The parser may interpret as US or European format
147
+ const parsedDate = new Date(result.mappedData[0].date);
148
+ expect(parsedDate.getDate()).toBeGreaterThanOrEqual(1);
149
+ expect(parsedDate.getMonth()).toBeGreaterThanOrEqual(0);
150
+ });
151
+
152
+ it('parses ISO date format (YYYY-MM-DD)', () => {
153
+ const csvData = [{ 'date': '2025-12-31' }];
154
+ const result = flexibleImport(csvData, ['date']);
155
+
156
+ const parsedDate = new Date(result.mappedData[0].date);
157
+ expect(parsedDate.getFullYear()).toBe(2025);
158
+ expect(parsedDate.getMonth()).toBe(11);
159
+ expect(parsedDate.getDate()).toBe(31);
160
+ });
161
+
162
+ it('parses date with time', () => {
163
+ const csvData = [{ 'date': '2025-12-31T10:30:00' }];
164
+ const result = flexibleImport(csvData, ['date']);
165
+
166
+ const parsedDate = new Date(result.mappedData[0].date);
167
+ expect(parsedDate.getHours()).toBe(10);
168
+ expect(parsedDate.getMinutes()).toBe(30);
169
+ });
170
+
171
+ it('handles natural language dates', () => {
172
+ const csvData = [
173
+ { 'date': '31 Dec 2025' },
174
+ { 'date': 'Dec 31, 2025' }
175
+ ];
176
+
177
+ const result = flexibleImport(csvData, ['date']);
178
+
179
+ result.mappedData.forEach(row => {
180
+ const parsedDate = new Date(row.date);
181
+ expect(parsedDate.getFullYear()).toBe(2025);
182
+ expect(parsedDate.getMonth()).toBe(11);
183
+ expect(parsedDate.getDate()).toBe(31);
184
+ });
185
+ });
186
+
187
+ it('handles 2-digit years', () => {
188
+ const csvData = [
189
+ { 'date': '12/31/25' } // US format
190
+ ];
191
+
192
+ const result = flexibleImport(csvData, ['date']);
193
+
194
+ const parsedDate = new Date(result.mappedData[0].date);
195
+ expect(parsedDate.getFullYear()).toBe(2025);
196
+ });
197
+
198
+ it('handles invalid dates gracefully', () => {
199
+ const csvData = [{ 'date': 'invalid-date' }];
200
+ const result = flexibleImport(csvData, ['date']);
201
+
202
+ // Should return the original string or empty
203
+ expect(typeof result.mappedData[0].date).toBe('string');
204
+ });
205
+
206
+ it('skips date parsing for non-date fields', () => {
207
+ const csvData = [{ 'field': '123456' }];
208
+ const result = flexibleImport(csvData, ['field']);
209
+
210
+ // May parse as number or string depending on field name
211
+ expect(result.mappedData[0].field).toBeDefined();
212
+ });
213
+ });
214
+
215
+ describe('Data Type Parsing', () => {
216
+ it('parses booleans correctly', () => {
217
+ const csvData = [
218
+ { 'is_active': 'true' },
219
+ { 'is_active': 'false' }
220
+ ];
221
+
222
+ const result = flexibleImport(csvData, ['is_active']);
223
+
224
+ expect(result.mappedData[0].is_active).toBe(true);
225
+ expect(result.mappedData[1].is_active).toBe(false);
226
+ });
227
+
228
+ it('parses numbers correctly', () => {
229
+ const csvData = [
230
+ { 'quantity': '123' },
231
+ { 'price': '45.67' }
232
+ ];
233
+
234
+ const result = flexibleImport(csvData, ['quantity', 'price']);
235
+
236
+ // Numbers may be parsed as strings or numbers depending on parsing logic
237
+ expect(result.mappedData[0].quantity).toBeDefined();
238
+ expect(result.mappedData[0].price).toBeDefined();
239
+ });
240
+
241
+ it('parses arrays from comma-separated values', () => {
242
+ const csvData = [
243
+ { 'tags': 'tag1,tag2,tag3', 'categories': 'cat1, cat2' }
244
+ ];
245
+
246
+ const result = flexibleImport(csvData, ['tags', 'categories'], {
247
+ columnMappings: {
248
+ tags: ['tags'],
249
+ categories: ['categories']
250
+ }
251
+ });
252
+
253
+ // Array parsing may or may not work depending on field name and parsing logic
254
+ expect(result.mappedData[0].tags).toBeDefined();
255
+ expect(result.mappedData[0].categories).toBeDefined();
256
+ });
257
+
258
+ it('handles null and undefined values', () => {
259
+ const csvData = [
260
+ { 'field1': null, 'field2': undefined as any, 'field3': '' }
261
+ ];
262
+
263
+ const result = flexibleImport(csvData, ['field1', 'field2', 'field3']);
264
+
265
+ expect(result.mappedData[0].field1).toBe('');
266
+ expect(result.mappedData[0].field2).toBe('');
267
+ expect(result.mappedData[0].field3).toBe('');
268
+ });
269
+ });
270
+
271
+ describe('Predefined Mappings', () => {
272
+ it('uses meal mappings correctly', () => {
273
+ const csvData = [
274
+ { 'Meal code': 'PTRY', 'Meal type': 'Pantry', 'Meal date': '26/9/2025' }
275
+ ];
276
+
277
+ const result = flexibleImport(csvData, ['meal_code', 'mealtype_name', 'meal_date'], {
278
+ columnMappings: commonMappings.meal
279
+ });
280
+
281
+ expect(result.mappedData[0].meal_code).toBe('PTRY');
282
+ expect(result.mappedData[0].mealtype_name).toBe('Pantry');
283
+ });
284
+
285
+ it('uses user mappings correctly', () => {
286
+ const csvData = [
287
+ { 'ID': '1', 'Full Name': 'John Doe', 'email': 'john@example.com' }
288
+ ];
289
+
290
+ const result = flexibleImport(csvData, ['id', 'name', 'email'], {
291
+ columnMappings: commonMappings.user
292
+ });
293
+
294
+ expect(result.mappedData[0].id).toBeDefined();
295
+ expect(result.mappedData[0].name).toBeDefined();
296
+ expect(result.mappedData[0].email).toBeDefined();
297
+ });
298
+
299
+ it('uses event mappings correctly', () => {
300
+ const csvData = [
301
+ { 'Event ID': '1', 'Event Name': 'Test Event', 'Date': '2025-01-01' }
302
+ ];
303
+
304
+ const result = flexibleImport(csvData, ['id', 'name', 'date'], {
305
+ columnMappings: commonMappings.event
306
+ });
307
+
308
+ expect(result.mappedData[0].id).toBeDefined();
309
+ expect(result.mappedData[0].name).toBeDefined();
310
+ });
311
+ });
312
+
313
+ describe('Security and Validation', () => {
314
+ it('prevents importing more than maxRows', () => {
315
+ const largeData = Array.from({ length: 10001 }, (_, i) => ({
316
+ 'Meal code': `meal${i}`, 'Meal type': 'type', 'Meal date': '2025-01-01'
317
+ }));
318
+
319
+ const result = flexibleImport(largeData, mockTargetFields, { maxRows: 10000 });
320
+
321
+ expect(result.errors).toHaveLength(1);
322
+ expect(result.errors[0].message).toContain('maximum row limit');
323
+ expect(result.mappedData).toEqual([]);
324
+ });
325
+
326
+ it('sanitizes control characters in strings', () => {
327
+ const csvData = [
328
+ { 'Meal code': 'test\x00\x01control\x02char' }
329
+ ];
330
+
331
+ const result = flexibleImport(csvData, ['meal_code'], {
332
+ allowControlChars: false,
333
+ columnMappings: {
334
+ meal_code: ['Meal code']
335
+ }
336
+ });
337
+
338
+ expect(result.mappedData[0].meal_code).toBe('testcontrolchar');
339
+ });
340
+
341
+ it('truncates strings longer than maxStringLength', () => {
342
+ const longString = 'a'.repeat(20000);
343
+ const csvData = [{ 'Meal code': longString }];
344
+
345
+ const result = flexibleImport(csvData, ['meal_code'], {
346
+ maxStringLength: 100,
347
+ columnMappings: {
348
+ meal_code: ['Meal code']
349
+ }
350
+ });
351
+
352
+ expect(String(result.mappedData[0].meal_code).length).toBe(100);
353
+ });
354
+
355
+ it('validates required fields', () => {
356
+ const csvData = [{ 'Meal code': '' }];
357
+
358
+ const result = flexibleImport(csvData, ['Meal code'], {
359
+ schemaValidation: {
360
+ 'Meal code': { required: true }
361
+ }
362
+ });
363
+
364
+ expect(result.errors.length).toBeGreaterThan(0);
365
+ expect(result.errors[0].field).toBe('Meal code');
366
+ expect(result.errors[0].message).toContain('required');
367
+ });
368
+
369
+ it('validates field types', () => {
370
+ const csvData = [{ 'age': 'not-a-number' }];
371
+
372
+ const result = flexibleImport(csvData, ['age'], {
373
+ schemaValidation: {
374
+ age: { type: 'number' }
375
+ }
376
+ });
377
+
378
+ expect(result.errors.length).toBeGreaterThan(0);
379
+ });
380
+
381
+ it('validates number ranges', () => {
382
+ const csvData = [{ 'age': '200' }];
383
+
384
+ const result = flexibleImport(csvData, ['age'], {
385
+ schemaValidation: {
386
+ age: { type: 'number', min: 0, max: 150 }
387
+ }
388
+ });
389
+
390
+ expect(result.errors.length).toBeGreaterThan(0);
391
+ });
392
+
393
+ it('validates pattern matching', () => {
394
+ const csvData = [{ 'email': 'not-an-email' }];
395
+
396
+ const result = flexibleImport(csvData, ['email'], {
397
+ schemaValidation: {
398
+ email: { pattern: /^[\w\.-]+@[\w\.-]+\.\w+$/ }
399
+ }
400
+ });
401
+
402
+ expect(result.errors.length).toBeGreaterThan(0);
403
+ });
404
+
405
+ it('allows rows with validation errors to be excluded', () => {
406
+ const csvData = [
407
+ { 'Meal code': 'valid', 'age': '30' },
408
+ { 'Meal code': '', 'age': 'invalid' }
409
+ ];
410
+
411
+ const result = flexibleImport(csvData, ['Meal code', 'age'], {
412
+ schemaValidation: {
413
+ 'Meal code': { required: true },
414
+ 'age': { type: 'number' }
415
+ }
416
+ });
417
+
418
+ // First row should pass, second should fail but be excluded
419
+ expect(result.stats.errorRows).toBe(1);
420
+ expect(result.mappedData.length).toBe(1);
421
+ });
422
+ });
423
+
424
+ describe('Edge Cases', () => {
425
+ it('handles empty CSV data', () => {
426
+ const result = flexibleImport([], mockTargetFields);
427
+
428
+ expect(result.mappedData).toEqual([]);
429
+ expect(result.stats.totalRows).toBe(0);
430
+ expect(result.stats.successfulRows).toBe(0);
431
+ });
432
+
433
+ it('handles CSV data with no matching columns', () => {
434
+ const csvData = [{ 'Unrelated': 'Data' }];
435
+ const result = flexibleImport(csvData, mockTargetFields);
436
+
437
+ expect(result.mappedData[0].meal_code).toBe('');
438
+ expect(result.mappedData[0].mealtype_name).toBe('');
439
+ });
440
+
441
+ it('handles very large CSV files', () => {
442
+ const largeData = Array.from({ length: 1000 }, (_, i) => ({
443
+ 'field': `value${i}`
444
+ }));
445
+
446
+ const result = flexibleImport(largeData, ['field']);
447
+
448
+ expect(result.mappedData.length).toBe(1000);
449
+ expect(result.stats.totalRows).toBe(1000);
450
+ });
451
+
452
+ it('handles special characters in data', () => {
453
+ const csvData = [
454
+ { 'field': 'Test "quotes" and \'apostrophes\'' },
455
+ { 'field': 'Line1\nLine2' }
456
+ ];
457
+
458
+ const result = flexibleImport(csvData, ['field']);
459
+
460
+ expect(result.mappedData[0].field).toContain('quotes');
461
+ expect(result.mappedData[1].field).toContain('Line1');
462
+ });
463
+
464
+ it('handles empty strings and whitespace', () => {
465
+ const csvData = [
466
+ { 'field1': ' ', 'field2': '', 'field3': 'valid' }
467
+ ];
468
+
469
+ const result = flexibleImport(csvData, ['field1', 'field2', 'field3']);
470
+
471
+ expect(result.mappedData[0].field1).toBe(' ');
472
+ expect(result.mappedData[0].field2).toBe('');
473
+ expect(result.mappedData[0].field3).toBe('valid');
474
+ });
475
+ });
476
+
477
+ describe('Statistics', () => {
478
+ it('returns correct statistics', () => {
479
+ const result = flexibleImport(mockCSVData, mockTargetFields);
480
+
481
+ expect(result.stats.totalRows).toBe(2);
482
+ expect(result.stats.successfulRows).toBe(2);
483
+ expect(result.stats.errorRows).toBe(0);
484
+ expect(result.stats.warningRows).toBe(0);
485
+ });
486
+
487
+ it('tracks errors when validation fails', () => {
488
+ const result = flexibleImport(mockCSVData, mockTargetFields);
489
+
490
+ // This would require specific validation rules to test error tracking
491
+ expect(Array.isArray(result.errors)).toBe(true);
492
+ });
493
+
494
+ it('tracks warnings for data quality issues', () => {
495
+ const result = flexibleImport(mockCSVData, mockTargetFields);
496
+
497
+ expect(Array.isArray(result.warnings)).toBe(true);
498
+ });
499
+ });
500
+
501
+ describe('DATE_FORMAT_SETS', () => {
502
+ it('defines US date formats', () => {
503
+ expect(DATE_FORMAT_SETS.US).toBeDefined();
504
+ expect(Array.isArray(DATE_FORMAT_SETS.US)).toBe(true);
505
+ expect(DATE_FORMAT_SETS.US.length).toBeGreaterThan(0);
506
+ });
507
+
508
+ it('defines European date formats', () => {
509
+ expect(DATE_FORMAT_SETS.EUROPEAN).toBeDefined();
510
+ expect(Array.isArray(DATE_FORMAT_SETS.EUROPEAN)).toBe(true);
511
+ });
512
+
513
+ it('defines ISO date formats', () => {
514
+ expect(DATE_FORMAT_SETS.ISO).toBeDefined();
515
+ expect(Array.isArray(DATE_FORMAT_SETS.ISO)).toBe(true);
516
+ });
517
+
518
+ it('defines ALL date formats', () => {
519
+ expect(DATE_FORMAT_SETS.ALL).toBeDefined();
520
+ expect(Array.isArray(DATE_FORMAT_SETS.ALL)).toBe(true);
521
+ expect(DATE_FORMAT_SETS.ALL.length).toBeGreaterThan(
522
+ DATE_FORMAT_SETS.US.length
523
+ );
524
+ });
525
+ });
526
+
527
+ describe('suggestDateFormats', () => {
528
+ it('suggests formats for US-style dates', () => {
529
+ const samples = ['12/31/2025', '1/1/2025', '01/15/2024'];
530
+ const suggestions = suggestDateFormats(samples);
531
+
532
+ expect(suggestions.length).toBeGreaterThan(0);
533
+ // May suggest either MM/DD/YYYY or DD/MM/YYYY
534
+ expect(['MM/DD/YYYY', 'DD/MM/YYYY']).toContain(suggestions[0]);
535
+ });
536
+
537
+ it('suggests formats for European-style dates', () => {
538
+ const samples = ['31/12/2025'];
539
+ const suggestions = suggestDateFormats(samples);
540
+
541
+ expect(suggestions.length).toBeGreaterThan(0);
542
+ // Should suggest DD/MM format since day is > 12
543
+ expect(suggestions.some(s => s.includes('DD'))).toBe(true);
544
+ });
545
+
546
+ it('suggests formats for ISO dates', () => {
547
+ const samples = ['2025-12-31', '2025-01-01'];
548
+ const suggestions = suggestDateFormats(samples);
549
+
550
+ expect(suggestions).toContain('YYYY-MM-DD');
551
+ });
552
+
553
+ it('returns empty array for invalid dates', () => {
554
+ const suggestions = suggestDateFormats(['invalid', 'not-a-date']);
555
+
556
+ expect(suggestions.length).toBe(0);
557
+ });
558
+
559
+ it('handles empty input', () => {
560
+ const suggestions = suggestDateFormats([]);
561
+
562
+ expect(suggestions).toEqual([]);
563
+ });
564
+
565
+ it('handles mixed date formats', () => {
566
+ const samples = ['12/31/2025', '31/12/2025', '2025-12-31'];
567
+ const suggestions = suggestDateFormats(samples);
568
+
569
+ expect(suggestions.length).toBeGreaterThanOrEqual(3);
570
+ });
571
+ });
572
+ });
573
+