@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
@@ -13,7 +13,12 @@
13
13
  * - Data sanitization
14
14
  * - Custom filename support
15
15
  * - Browser download handling
16
- *
16
+ */
17
+
18
+ import { createLogger } from '../../../utils/logger';
19
+ import type { DataRecord } from '../types';
20
+
21
+ /**
17
22
  * @example
18
23
  * ```tsx
19
24
  * // Basic export
@@ -37,6 +42,73 @@
37
42
  * ```
38
43
  */
39
44
 
45
+ /**
46
+ * Column definition for export
47
+ */
48
+ export interface ExportColumn {
49
+ header?: string;
50
+ id?: string;
51
+ accessorKey?: string;
52
+ }
53
+
54
+ /**
55
+ * Escapes a value for CSV format according to RFC 4180
56
+ * - Encloses in quotes if value contains comma, newline, or quote
57
+ * - Escapes existing quotes by doubling them
58
+ * - Prevents CSV injection by sanitizing dangerous characters
59
+ */
60
+ function escapeCSVValue(value: unknown, sanitizeForSecurity: boolean = true): string {
61
+ if (value === null || value === undefined) {
62
+ return sanitizeForSecurity ? '""' : '';
63
+ }
64
+
65
+ let stringValue = String(value);
66
+
67
+ if (sanitizeForSecurity) {
68
+ // Sanitize to prevent CSV injection
69
+ // Check for dangerous patterns that could be interpreted as formulas
70
+ // If starts with =, +, -, @, or \t, it could be a formula
71
+ if (/^[=+\-@]/.test(stringValue) || stringValue.startsWith('\t')) {
72
+ // Prefix with single quote to prevent formula interpretation in Excel
73
+ stringValue = "'" + stringValue;
74
+ }
75
+
76
+ // Remove control characters except newline (which is handled below)
77
+ stringValue = stringValue.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F]/g, '');
78
+
79
+ // Always quote values for consistency and safety
80
+ const escaped = stringValue.replace(/"/g, '""');
81
+ return `"${escaped}"`;
82
+ } else {
83
+ // Minimal escaping - only escape quotes in quoted strings
84
+ const escaped = stringValue.replace(/"/g, '""');
85
+ return `"${escaped}"`;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Formats a value according to locale preferences
91
+ */
92
+ function formatLocaleValue(value: unknown, locale?: string): string {
93
+ if (value === null || value === undefined) {
94
+ return '';
95
+ }
96
+
97
+ if (typeof value === 'number') {
98
+ return new Intl.NumberFormat(locale).format(value);
99
+ }
100
+
101
+ if (value instanceof Date) {
102
+ return new Intl.DateTimeFormat(locale).format(value);
103
+ }
104
+
105
+ if (typeof value === 'boolean') {
106
+ return ''; // Don't format booleans in formatLocaleValue, let them pass through as-is
107
+ }
108
+
109
+ return String(value);
110
+ }
111
+
40
112
  /**
41
113
  * Generates CSV content from data without triggering download
42
114
  *
@@ -45,26 +117,42 @@
45
117
  * @param options - Export options
46
118
  * @returns CSV content as string
47
119
  */
48
- export function generateCSVContent<TData>(
120
+ export function generateCSVContent<TData extends DataRecord>(
49
121
  data: TData[],
50
- columns: any[],
51
- options: { includeHeaders?: boolean } = {}
122
+ columns: ExportColumn[],
123
+ options: {
124
+ includeHeaders?: boolean;
125
+ locale?: string;
126
+ sanitizeForSecurity?: boolean; // Default: true
127
+ } = {}
52
128
  ): string {
53
129
  if (!data.length) return '';
54
130
 
55
- const { includeHeaders = true } = options;
131
+ const {
132
+ includeHeaders = true,
133
+ locale,
134
+ sanitizeForSecurity = true
135
+ } = options;
56
136
 
57
137
  // Create CSV header row
58
- const headers = columns.map(col => col.header || col.id || "Column");
138
+ const headers = columns.map(col => {
139
+ const headerValue = col.header || col.id || "Column";
140
+ return escapeCSVValue(headerValue, sanitizeForSecurity);
141
+ });
59
142
 
60
143
  // Format data into CSV rows
61
144
  const csvData = data.map(row => {
62
145
  return columns.map(col => {
63
146
  const key = col.accessorKey || col.id;
64
- const value = (row as any)[key];
65
- // Escape commas and quotes
66
- const escapedValue = String(value || '').replace(/"/g, '""');
67
- return `"${escapedValue}"`;
147
+ let value = key ? row[key] : undefined;
148
+
149
+ // Format according to locale if provided
150
+ if (locale && (typeof value === 'number' || value instanceof Date || typeof value === 'boolean')) {
151
+ value = formatLocaleValue(value, locale);
152
+ }
153
+
154
+ // Escape the value for CSV
155
+ return escapeCSVValue(value, sanitizeForSecurity);
68
156
  }).join(",");
69
157
  });
70
158
 
@@ -82,6 +170,8 @@ export function generateCSVContent<TData>(
82
170
  * @param data - Array of data objects to export
83
171
  * @param columns - Column definitions for mapping
84
172
  * @param filename - Optional filename for download (default: "download.csv")
173
+ * @param options - Optional export configuration
174
+ * @returns Promise that resolves when export is complete, rejects on error
85
175
  *
86
176
  * @example
87
177
  * ```tsx
@@ -96,7 +186,12 @@ export function generateCSVContent<TData>(
96
186
  * { accessorKey: 'email', header: 'Email' }
97
187
  * ];
98
188
  *
99
- * exportToCSV(users, columns, 'users.csv');
189
+ * try {
190
+ * await exportToCSV(users, columns, 'users.csv', { locale: 'en-US' });
191
+ * showSuccessToast('Data exported successfully');
192
+ * } catch (error) {
193
+ * showErrorToast('Failed to export data');
194
+ * }
100
195
  * ```
101
196
  *
102
197
  * @remarks
@@ -104,23 +199,57 @@ export function generateCSVContent<TData>(
104
199
  * - Uses column headers for CSV headers
105
200
  * - Sanitizes data to prevent CSV injection
106
201
  * - Triggers browser download
202
+ * - Throws error if export fails
107
203
  */
108
- export function exportToCSV<TData>(
204
+ export function exportToCSV<TData extends DataRecord>(
109
205
  data: TData[],
110
- columns: any[],
111
- filename: string = "download.csv"
112
- ): void {
113
- const csvContent = generateCSVContent(data, columns);
114
-
115
- // Create and trigger download
116
- const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
117
- const link = document.createElement("a");
118
- const url = URL.createObjectURL(blob);
119
-
120
- link.setAttribute("href", url);
121
- link.setAttribute("download", filename);
122
- link.style.display = "none";
123
- document.body.appendChild(link);
124
- link.click();
125
- document.body.removeChild(link);
206
+ columns: ExportColumn[],
207
+ filename: string = "download.csv",
208
+ options: {
209
+ locale?: string;
210
+ sanitizeForSecurity?: boolean;
211
+ } = {}
212
+ ): Promise<void> {
213
+ const logger = createLogger('ExportUtils');
214
+ return new Promise((resolve, reject) => {
215
+ try {
216
+ if (typeof window === 'undefined') {
217
+ throw new Error('CSV export is only available in browser environments');
218
+ }
219
+
220
+ if (!data || data.length === 0) {
221
+ throw new Error('No data to export');
222
+ }
223
+
224
+ if (!columns || columns.length === 0) {
225
+ throw new Error('No columns defined for export');
226
+ }
227
+
228
+ const csvContent = generateCSVContent(data, columns, options);
229
+
230
+ // Create and trigger download
231
+ const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
232
+ const link = document.createElement("a");
233
+ const url = URL.createObjectURL(blob);
234
+
235
+ link.setAttribute("href", url);
236
+ link.setAttribute("download", filename);
237
+ link.style.display = "none";
238
+
239
+ // Handle click event
240
+ link.onclick = () => {
241
+ setTimeout(() => {
242
+ URL.revokeObjectURL(url);
243
+ resolve();
244
+ }, 100);
245
+ };
246
+
247
+ document.body.appendChild(link);
248
+ link.click();
249
+ document.body.removeChild(link);
250
+ } catch (error) {
251
+ logger.error('Failed to export data to CSV:', error);
252
+ reject(error);
253
+ }
254
+ });
126
255
  }
@@ -8,6 +8,9 @@
8
8
  * This utility can be used across different data tables with different column structures.
9
9
  */
10
10
 
11
+ import { createLogger } from '../../../utils/logger';
12
+ import type { CellValue } from '../types';
13
+
11
14
  export interface ColumnMapping {
12
15
  [targetField: string]: string[];
13
16
  }
@@ -16,9 +19,20 @@ export interface ImportOptions {
16
19
  columnMappings?: ColumnMapping;
17
20
  dateFormats?: readonly string[];
18
21
  dateFormatHints?: Record<string, readonly string[]>; // Field-specific date format hints
19
- defaultValues?: Record<string, any>;
22
+ defaultValues?: Record<string, CellValue>;
20
23
  timezone?: string; // Default timezone for date parsing
21
24
  strictDateParsing?: boolean; // Whether to be strict about date format matching
25
+ maxRows?: number; // Maximum number of rows to import (default: 10000)
26
+ maxStringLength?: number; // Maximum length for string values (default: 10000)
27
+ allowControlChars?: boolean; // Whether to allow control characters (default: false)
28
+ schemaValidation?: Record<string, {
29
+ required?: boolean;
30
+ type?: 'string' | 'number' | 'boolean' | 'date';
31
+ min?: number;
32
+ max?: number;
33
+ pattern?: RegExp;
34
+ allowEmpty?: boolean;
35
+ }>; // Schema validation rules
22
36
  }
23
37
 
24
38
  // Predefined date format sets for common use cases
@@ -140,15 +154,117 @@ export const DATE_FORMAT_SETS = {
140
154
  * });
141
155
  * ```
142
156
  */
143
- export function flexibleImport<T = any>(
144
- csvData: any[],
157
+ export interface ImportError {
158
+ row: number;
159
+ field: string;
160
+ message: string;
161
+ }
162
+
163
+ export interface ImportWarning {
164
+ row: number;
165
+ field: string;
166
+ message: string;
167
+ }
168
+
169
+ /**
170
+ * Sanitizes a string value by removing or escaping dangerous characters
171
+ */
172
+ function sanitizeString(value: string, options: ImportOptions): string {
173
+ const maxLength = options.maxStringLength || 10000;
174
+ let sanitized = value;
175
+
176
+ // Truncate to max length
177
+ if (sanitized.length > maxLength) {
178
+ sanitized = sanitized.substring(0, maxLength);
179
+ }
180
+
181
+ // Remove control characters if not allowed
182
+ if (!options.allowControlChars) {
183
+ // Keep tabs, newlines, carriage returns
184
+ sanitized = sanitized.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/g, '');
185
+ }
186
+
187
+ // Remove null bytes
188
+ sanitized = sanitized.replace(/\0/g, '');
189
+
190
+ // Remove SQL injection attempts
191
+ sanitized = sanitized.replace(/['";]/g, (char) => {
192
+ // Escape quotes and semicolons
193
+ return char === '"' ? '&quot;' : char === "'" ? '&apos;' : '&semi;';
194
+ });
195
+
196
+ return sanitized;
197
+ }
198
+
199
+ /**
200
+ * Validates a value against schema rules
201
+ */
202
+ function validateValue(field: string, value: unknown, rules: ImportOptions['schemaValidation']): {
203
+ valid: boolean;
204
+ message?: string;
205
+ } {
206
+ if (!rules || !rules[field]) {
207
+ return { valid: true };
208
+ }
209
+
210
+ const rule = rules[field];
211
+
212
+ // Check required
213
+ if (rule.required && (value === null || value === undefined || value === '')) {
214
+ return { valid: false, message: `Field '${field}' is required` };
215
+ }
216
+
217
+ // Check empty allowed
218
+ if (!rule.allowEmpty && value === '') {
219
+ return { valid: false, message: `Field '${field}' cannot be empty` };
220
+ }
221
+
222
+ // Check type
223
+ if (rule.type) {
224
+ if (rule.type === 'number') {
225
+ if (typeof value !== 'number' && (typeof value !== 'string' || isNaN(Number(value)))) {
226
+ return { valid: false, message: `Field '${field}' must be a number` };
227
+ }
228
+ const numValue = typeof value === 'number' ? value : Number(value);
229
+
230
+ if (rule.min !== undefined && numValue < rule.min) {
231
+ return { valid: false, message: `Field '${field}' must be at least ${rule.min}` };
232
+ }
233
+
234
+ if (rule.max !== undefined && numValue > rule.max) {
235
+ return { valid: false, message: `Field '${field}' must be at most ${rule.max}` };
236
+ }
237
+ } else if (rule.type === 'boolean') {
238
+ if (typeof value !== 'boolean' && value !== 'true' && value !== 'false') {
239
+ return { valid: false, message: `Field '${field}' must be a boolean` };
240
+ }
241
+ } else if (rule.type === 'date') {
242
+ const dateValue = typeof value === 'string' ? new Date(value) : value;
243
+ if (!(dateValue instanceof Date) || isNaN(dateValue.getTime())) {
244
+ return { valid: false, message: `Field '${field}' must be a valid date` };
245
+ }
246
+ }
247
+ }
248
+
249
+ // Check pattern
250
+ if (rule.pattern && typeof value === 'string') {
251
+ if (!rule.pattern.test(value)) {
252
+ return { valid: false, message: `Field '${field}' does not match required pattern` };
253
+ }
254
+ }
255
+
256
+ return { valid: true };
257
+ }
258
+
259
+ export function flexibleImport<T extends Record<string, unknown>>(
260
+ csvData: Record<string, unknown>[],
145
261
  targetFields: string[],
146
262
  options: ImportOptions = {}
147
263
  ): {
148
264
  mappedData: T[];
149
265
  mappings: Record<string, string | null>;
150
- errors: any[];
151
- warnings: any[];
266
+ errors: ImportError[];
267
+ warnings: ImportWarning[];
152
268
  stats: {
153
269
  totalRows: number;
154
270
  successfulRows: number;
@@ -156,9 +272,34 @@ export function flexibleImport<T = any>(
156
272
  warningRows: number;
157
273
  };
158
274
  } {
159
- console.log('🔄 Starting flexible import...');
160
- console.log('🔄 Available CSV columns:', Object.keys(csvData[0] || {}));
161
- console.log('🔄 Target fields:', targetFields);
275
+ const logger = createLogger('FlexibleImport');
276
+ logger.info('Starting flexible import...');
277
+ logger.debug('Available CSV columns:', Object.keys(csvData[0] || {}));
278
+ logger.debug('Target fields:', targetFields);
279
+
280
+ const maxRows = options.maxRows || 10000;
281
+
282
+ // Check row limit
283
+ if (csvData.length > maxRows) {
284
+ const error: ImportError = {
285
+ row: 0,
286
+ field: 'global',
287
+ message: `Import exceeds maximum row limit of ${maxRows}. Found ${csvData.length} rows.`
288
+ };
289
+ logger.error('Import exceeds row limit', { rowCount: csvData.length, limit: maxRows });
290
+ return {
291
+ mappedData: [],
292
+ mappings: {},
293
+ errors: [error],
294
+ warnings: [],
295
+ stats: {
296
+ totalRows: csvData.length,
297
+ successfulRows: 0,
298
+ errorRows: csvData.length,
299
+ warningRows: 0
300
+ }
301
+ };
302
+ }
162
303
 
163
304
  // Default column mappings
164
305
  const defaultMappings: ColumnMapping = {
@@ -192,11 +333,11 @@ export function flexibleImport<T = any>(
192
333
  possibleName.toLowerCase().includes(col.toLowerCase())
193
334
  );
194
335
  if (found) {
195
- console.log(`✅ Mapped ${targetField} -> ${found}`);
336
+ logger.debug(`Mapped ${targetField} -> ${found}`);
196
337
  return found;
197
338
  }
198
339
  }
199
- console.log(`❌ No mapping found for ${targetField}`);
340
+ logger.warn(`No mapping found for ${targetField}`);
200
341
  return null;
201
342
  };
202
343
 
@@ -206,7 +347,7 @@ export function flexibleImport<T = any>(
206
347
  mappings[field] = findColumn(field);
207
348
  });
208
349
 
209
- console.log('🔄 Column mappings:', mappings);
350
+ logger.debug('Column mappings:', mappings);
210
351
 
211
352
  // Enhanced date parsing with multiple format support
212
353
  const parseDate = (dateStr: string, fieldName?: string): Date | null => {
@@ -320,16 +461,19 @@ export function flexibleImport<T = any>(
320
461
 
321
462
 
322
463
  // Parse different data types
323
- const parseValue = (value: any, fieldName: string): any => {
464
+ const parseValue = (value: unknown, fieldName: string): unknown => {
324
465
  if (value === null || value === undefined || value === '') {
325
466
  return options.defaultValues?.[fieldName] || '';
326
467
  }
327
468
 
328
469
  // Auto-detect and parse different data types
329
470
  if (typeof value === 'string') {
471
+ // Sanitize string values
472
+ const sanitized = sanitizeString(value, options);
473
+
330
474
  // Try to parse as date first (for fields that might contain dates)
331
475
  if (fieldName.toLowerCase().includes('date') || fieldName.toLowerCase().includes('time')) {
332
- const dateValue = parseDate(value, fieldName);
476
+ const dateValue = parseDate(sanitized, fieldName);
333
477
  if (dateValue) {
334
478
  // Return ISO string for consistent date handling
335
479
  return dateValue.toISOString();
@@ -337,57 +481,83 @@ export function flexibleImport<T = any>(
337
481
  }
338
482
 
339
483
  // Try to parse as boolean
340
- if (value.toLowerCase() === 'true') return true;
341
- if (value.toLowerCase() === 'false') return false;
484
+ if (sanitized.toLowerCase() === 'true') return true;
485
+ if (sanitized.toLowerCase() === 'false') return false;
342
486
 
343
487
  // Try to parse as array (comma-separated)
344
- if (value.includes(',') && (fieldName.toLowerCase().includes('tag') || fieldName.toLowerCase().includes('category'))) {
345
- return value.split(',').map((item: string) => item.trim()).filter((item: string) => item);
488
+ if (sanitized.includes(',') && (fieldName.toLowerCase().includes('tag') || fieldName.toLowerCase().includes('category'))) {
489
+ return sanitized.split(',').map((item: string) => item.trim()).filter((item: string) => item);
346
490
  }
347
491
 
348
492
  // Try to parse as number (only if not a date field)
349
493
  if (!fieldName.toLowerCase().includes('date') && !fieldName.toLowerCase().includes('time')) {
350
- const numValue = parseFloat(value);
494
+ const numValue = parseFloat(sanitized);
351
495
  if (!isNaN(numValue) && isFinite(numValue)) {
352
496
  return numValue;
353
497
  }
354
498
  }
499
+
500
+ return sanitized;
355
501
  }
356
502
 
357
503
  return value;
358
504
  };
359
505
 
360
- const mappedData = csvData.map((row, index) => {
361
- console.log(`🔄 Processing row ${index + 1}:`, row);
506
+ const validationErrors: ImportError[] = [];
507
+ const validationWarnings: ImportWarning[] = [];
508
+ const mappedData: T[] = [];
509
+
510
+ for (let index = 0; index < csvData.length; index++) {
511
+ const row = csvData[index];
512
+ logger.debug(`Processing row ${index + 1}:`, row);
362
513
 
363
- const transformedRow: any = {};
514
+ const transformedRow: Record<string, unknown> = {};
515
+ let hasErrors = false;
364
516
 
365
517
  targetFields.forEach(field => {
366
518
  const sourceColumn = mappings[field];
519
+ let value: unknown;
520
+
367
521
  if (sourceColumn && row[sourceColumn] !== undefined) {
368
- transformedRow[field] = parseValue(row[sourceColumn], field);
522
+ value = parseValue(row[sourceColumn], field);
369
523
  } else {
370
- transformedRow[field] = options.defaultValues?.[field] || '';
524
+ value = options.defaultValues?.[field] || '';
525
+ }
526
+
527
+ transformedRow[field] = value;
528
+
529
+ // Validate against schema
530
+ const validation = validateValue(field, value, options.schemaValidation);
531
+ if (!validation.valid) {
532
+ validationErrors.push({
533
+ row: index + 1,
534
+ field,
535
+ message: validation.message || `Validation failed for field '${field}'`
536
+ });
537
+ hasErrors = true;
371
538
  }
372
539
  });
373
540
 
374
- console.log(`🔄 Transformed row ${index + 1}:`, transformedRow);
375
- return transformedRow;
376
- });
377
-
378
- // Validation and error handling
379
- const validationErrors: any[] = [];
380
- const validationWarnings: any[] = [];
541
+ // Only add row if validation passed (or if no validation rules)
542
+ if (!options.schemaValidation || !hasErrors) {
543
+ mappedData.push(transformedRow as T);
544
+ }
545
+
546
+ logger.debug(`Transformed row ${index + 1}:`, transformedRow);
547
+ }
381
548
 
549
+ // Calculate unique error rows (multiple errors per row still count as 1 error row)
550
+ const errorRowSet = new Set(validationErrors.map(e => e.row));
551
+
382
552
  return {
383
- mappedData,
553
+ mappedData: mappedData,
384
554
  mappings,
385
555
  errors: validationErrors,
386
556
  warnings: validationWarnings,
387
557
  stats: {
388
558
  totalRows: csvData.length,
389
559
  successfulRows: mappedData.length,
390
- errorRows: validationErrors.length,
560
+ errorRows: errorRowSet.size,
391
561
  warningRows: validationWarnings.length,
392
562
  }
393
563
  };
@@ -9,10 +9,57 @@ import type { HierarchicalDataRow } from '../types';
9
9
  import type { SortingState } from '@tanstack/react-table';
10
10
 
11
11
  /**
12
- * Sorts hierarchical data while preserving parent-child relationships
13
- * Parent rows maintain their order, child rows are sorted within their parent groups
12
+ * Sorts hierarchical data by structure only (parents before children).
13
+ * This is the basic hierarchical ordering without any column-based sorting.
14
+ *
15
+ * @param data - Array of hierarchical data rows
16
+ * @returns Data sorted with parents first, then their children
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * const sorted = sortHierarchicalDataByStructure([...data]);
21
+ * ```
14
22
  */
15
- export function sortHierarchicalData<TData extends HierarchicalDataRow>(
23
+ export function sortHierarchicalDataByStructure<TData extends HierarchicalDataRow>(
24
+ data: TData[]
25
+ ): TData[] {
26
+ const sorted: TData[] = [];
27
+ const processed = new Set<string>();
28
+
29
+ // First, add all parent rows
30
+ data.forEach(row => {
31
+ if (row.isParent && !processed.has(row.id)) {
32
+ sorted.push(row);
33
+ processed.add(row.id);
34
+ }
35
+ });
36
+
37
+ // Then, add children in order
38
+ data.forEach(row => {
39
+ if (!row.isParent && !processed.has(row.id)) {
40
+ sorted.push(row);
41
+ processed.add(row.id);
42
+ }
43
+ });
44
+
45
+ return sorted;
46
+ }
47
+
48
+ /**
49
+ * Sorts hierarchical data while preserving parent-child relationships.
50
+ * Parent rows maintain their order, child rows are sorted within their parent groups.
51
+ * This applies column-based sorting based on the provided sorting state.
52
+ *
53
+ * @param data - Array of hierarchical data rows
54
+ * @param sorting - TanStack Table sorting state (column + direction)
55
+ * @returns Data sorted with parents first, children sorted within each parent
56
+ *
57
+ * @example
58
+ * ```tsx
59
+ * const sorted = sortHierarchicalDataWithSorting(data, [{ id: 'name', desc: false }]);
60
+ * ```
61
+ */
62
+ export function sortHierarchicalDataWithSorting<TData extends HierarchicalDataRow>(
16
63
  data: TData[],
17
64
  sorting: SortingState
18
65
  ): TData[] {