@jmruthers/pace-core 0.6.6 → 0.6.8

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 (292) hide show
  1. package/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +227 -22
  2. package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
  3. package/audit-tool/audits/02-project-structure.cjs +240 -0
  4. package/audit-tool/audits/03-architecture.cjs +224 -0
  5. package/audit-tool/audits/04-code-quality.cjs +149 -0
  6. package/audit-tool/audits/05-styling.cjs +224 -0
  7. package/audit-tool/audits/06-security-rbac.cjs +554 -0
  8. package/audit-tool/audits/07-api-tech-stack.cjs +355 -0
  9. package/audit-tool/audits/08-testing-documentation.cjs +202 -0
  10. package/audit-tool/audits/09-operations.cjs +208 -0
  11. package/audit-tool/index.cjs +295 -0
  12. package/audit-tool/utils/code-utils.cjs +218 -0
  13. package/audit-tool/utils/file-utils.cjs +230 -0
  14. package/audit-tool/utils/report-utils.cjs +380 -0
  15. package/cursor-rules/00-standards-overview.mdc +156 -0
  16. package/cursor-rules/{00-pace-core-compliance.mdc → 01-pace-core-compliance.mdc} +187 -34
  17. package/cursor-rules/02-project-structure.mdc +37 -5
  18. package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +125 -11
  19. package/cursor-rules/04-code-quality.mdc +419 -0
  20. package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +55 -10
  21. package/cursor-rules/{09-rbac-compliance.mdc → 06-security-rbac.mdc} +62 -6
  22. package/cursor-rules/07-api-tech-stack.mdc +377 -0
  23. package/cursor-rules/08-testing-documentation.mdc +324 -0
  24. package/cursor-rules/09-operations.mdc +365 -0
  25. package/dist/DataTable-6RMSCQJ6.js +15 -0
  26. package/dist/{DataTable-2N_tqbfq.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
  27. package/dist/{PublicPageProvider-BBH6Vqg7.d.ts → PublicPageProvider-CIGSujI2.d.ts} +40 -24
  28. package/dist/{UnifiedAuthProvider-ZT6TIGM7.js → UnifiedAuthProvider-7SNDOWYD.js} +2 -2
  29. package/dist/{api-Y4MQWOFW.js → api-7P7DI652.js} +1 -1
  30. package/dist/{chunk-MAGBIDNS.js → chunk-4DDCYDQ3.js} +8 -7
  31. package/dist/{chunk-BVP2BCJF.js → chunk-5W2A3DRC.js} +10 -9
  32. package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
  33. package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
  34. package/dist/{chunk-3O3WHILE.js → chunk-EF2UGZWY.js} +239 -63
  35. package/dist/{chunk-LAZMKTTF.js → chunk-EURB7QFZ.js} +341 -337
  36. package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
  37. package/dist/{chunk-7TYHROIV.js → chunk-GS5672WG.js} +55 -13
  38. package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
  39. package/dist/{chunk-ZFYPMX46.js → chunk-LX6U42O3.js} +1 -1
  40. package/dist/{chunk-FENMYN2U.js → chunk-MPBLMWVR.js} +3 -3
  41. package/dist/{chunk-ZS5VO5JB.js → chunk-NKHKXPI4.js} +408 -453
  42. package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
  43. package/dist/{chunk-4T7OBVTU.js → chunk-S6ZQKDY6.js} +1 -1
  44. package/dist/{chunk-FTCRZOG2.js → chunk-T5CVK4R3.js} +5 -5
  45. package/dist/{chunk-OHIK3MIO.js → chunk-Z2FNRKF3.js} +13 -13
  46. package/dist/components.d.ts +5 -4
  47. package/dist/components.js +29 -34
  48. package/dist/eslint-rules/index.cjs +22 -9
  49. package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
  50. package/dist/eslint-rules/rules/04-code-quality.cjs +346 -0
  51. package/dist/eslint-rules/rules/05-styling.cjs +61 -0
  52. package/dist/eslint-rules/rules/{rbac.cjs → 06-security-rbac.cjs} +34 -13
  53. package/dist/eslint-rules/rules/07-api-tech-stack.cjs +385 -0
  54. package/dist/eslint-rules/rules/08-testing.cjs +94 -0
  55. package/dist/{functions-DHebl8-F.d.ts → functions-lBy5L2ry.d.ts} +1 -1
  56. package/dist/hooks.d.ts +5 -5
  57. package/dist/hooks.js +8 -8
  58. package/dist/index.d.ts +7 -7
  59. package/dist/index.js +21 -20
  60. package/dist/providers.js +2 -2
  61. package/dist/rbac/index.d.ts +1 -1
  62. package/dist/rbac/index.js +8 -8
  63. package/dist/theming/runtime.d.ts +61 -1
  64. package/dist/theming/runtime.js +1 -1
  65. package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
  66. package/dist/types.d.ts +2 -2
  67. package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
  68. package/dist/utils.d.ts +2 -2
  69. package/dist/utils.js +8 -8
  70. package/docs/README.md +1 -1
  71. package/docs/api/modules.md +106 -41
  72. package/docs/api-reference/components.md +18 -20
  73. package/docs/api-reference/hooks.md +80 -80
  74. package/docs/api-reference/types.md +1 -1
  75. package/docs/api-reference/utilities.md +1 -1
  76. package/docs/architecture/README.md +1 -1
  77. package/docs/core-concepts/events.md +3 -3
  78. package/docs/core-concepts/organisations.md +6 -6
  79. package/docs/core-concepts/permissions.md +6 -6
  80. package/docs/documentation-index.md +12 -18
  81. package/docs/getting-started/dependencies.md +23 -0
  82. package/docs/getting-started/documentation-index.md +1 -1
  83. package/docs/getting-started/examples/README.md +4 -4
  84. package/docs/getting-started/examples/full-featured-app.md +1 -1
  85. package/docs/getting-started/faq.md +2 -2
  86. package/docs/getting-started/quick-reference.md +4 -4
  87. package/docs/implementation-guides/app-layout.md +1 -1
  88. package/docs/implementation-guides/authentication.md +15 -15
  89. package/docs/implementation-guides/component-styling.md +1 -1
  90. package/docs/implementation-guides/data-tables.md +127 -34
  91. package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
  92. package/docs/implementation-guides/dynamic-colors.md +3 -3
  93. package/docs/implementation-guides/file-upload-storage.md +2 -2
  94. package/docs/implementation-guides/hierarchical-datatable.md +40 -60
  95. package/docs/implementation-guides/inactivity-tracking.md +3 -3
  96. package/docs/implementation-guides/large-datasets.md +3 -2
  97. package/docs/implementation-guides/organisation-security.md +2 -2
  98. package/docs/implementation-guides/performance.md +2 -2
  99. package/docs/implementation-guides/permission-enforcement.md +1 -1
  100. package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
  101. package/docs/migration/V0.4.0_rbac-migration.md +6 -6
  102. package/docs/rbac/README.md +5 -5
  103. package/docs/rbac/advanced-patterns.md +6 -6
  104. package/docs/rbac/api-reference.md +20 -20
  105. package/docs/rbac/event-based-apps.md +3 -3
  106. package/docs/rbac/examples.md +41 -41
  107. package/docs/rbac/getting-started.md +37 -37
  108. package/docs/rbac/performance.md +1 -1
  109. package/docs/rbac/quick-start.md +52 -52
  110. package/docs/rbac/secure-client-protection.md +1 -1
  111. package/docs/rbac/troubleshooting.md +1 -1
  112. package/docs/security/README.md +5 -5
  113. package/docs/standards/0-standards-overview.md +220 -0
  114. package/docs/standards/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +241 -185
  115. package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
  116. package/docs/standards/3-architecture-standards.md +606 -0
  117. package/docs/standards/4-code-quality-standards.md +728 -0
  118. package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
  119. package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
  120. package/docs/standards/7-api-tech-stack-standards.md +662 -0
  121. package/docs/standards/8-testing-documentation-standards.md +401 -0
  122. package/docs/standards/9-operations-standards.md +1102 -0
  123. package/docs/standards/README.md +203 -104
  124. package/docs/troubleshooting/README.md +4 -4
  125. package/docs/troubleshooting/common-issues.md +2 -2
  126. package/docs/troubleshooting/debugging.md +9 -9
  127. package/docs/troubleshooting/migration.md +4 -4
  128. package/eslint-config-pace-core.cjs +50 -20
  129. package/package.json +50 -19
  130. package/scripts/eslint-audit.cjs +123 -0
  131. package/scripts/install-cursor-rules.cjs +11 -243
  132. package/scripts/install-eslint-config.cjs +349 -0
  133. package/scripts/validate-dependencies.cjs +248 -0
  134. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
  135. package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
  136. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +30 -18
  137. package/src/__tests__/integration/UserProfile.test.tsx +14 -14
  138. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
  139. package/src/__tests__/templates/accessibility.test.template.tsx +10 -9
  140. package/src/__tests__/templates/component.test.template.tsx +18 -15
  141. package/src/components/AddressField/AddressField.tsx +26 -1
  142. package/src/components/Alert/Alert.test.tsx +86 -22
  143. package/src/components/Alert/Alert.tsx +19 -11
  144. package/src/components/Badge/Badge.tsx +1 -1
  145. package/src/components/Calendar/Calendar.tsx +201 -47
  146. package/src/components/Checkbox/Checkbox.test.tsx +2 -1
  147. package/src/components/ContextSelector/ContextSelector.tsx +108 -126
  148. package/src/components/DataTable/AUDIT_REPORT.md +293 -0
  149. package/src/components/DataTable/DataTable.tsx +1 -19
  150. package/src/components/DataTable/__tests__/DataTableCore.test.tsx +6 -2
  151. package/src/components/DataTable/__tests__/a11y.basic.test.tsx +21 -6
  152. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +3 -2
  153. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
  154. package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
  155. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
  156. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
  157. package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
  158. package/src/components/DataTable/components/EditableRow.tsx +5 -7
  159. package/src/components/DataTable/components/EmptyState.tsx +11 -10
  160. package/src/components/DataTable/components/FilterRow.tsx +2 -4
  161. package/src/components/DataTable/components/ImportModal.tsx +124 -126
  162. package/src/components/DataTable/components/LoadingState.tsx +5 -6
  163. package/src/components/DataTable/components/SortIndicator.tsx +50 -0
  164. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
  165. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
  166. package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
  167. package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
  168. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
  169. package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +45 -27
  170. package/src/components/DataTable/components/index.ts +2 -1
  171. package/src/components/DataTable/types.ts +0 -18
  172. package/src/components/DataTable/utils/a11yUtils.ts +17 -0
  173. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +1 -1
  174. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
  175. package/src/components/DateTimeField/DateTimeField.tsx +7 -8
  176. package/src/components/Dialog/Dialog.test.tsx +1 -0
  177. package/src/components/Dialog/Dialog.tsx +25 -8
  178. package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
  179. package/src/components/FileUpload/FileUpload.test.tsx +45 -16
  180. package/src/components/FileUpload/FileUpload.tsx +141 -130
  181. package/src/components/NavigationMenu/NavigationMenu.test.tsx +48 -12
  182. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +9 -9
  183. package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +30 -30
  184. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -4
  185. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +7 -1
  186. package/src/components/Progress/Progress.tsx +2 -4
  187. package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
  188. package/src/components/Select/Select.tsx +86 -77
  189. package/src/components/Select/types.ts +3 -0
  190. package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
  191. package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
  192. package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +8 -5
  193. package/src/hooks/__tests__/useFileUrl.unit.test.ts +4 -0
  194. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +99 -99
  195. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +45 -8
  196. package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +22 -2
  197. package/src/hooks/public/usePublicEvent.ts +5 -5
  198. package/src/hooks/public/usePublicEventLogo.ts +5 -5
  199. package/src/hooks/public/usePublicFileDisplay.ts +2 -2
  200. package/src/hooks/public/usePublicRouteParams.ts +13 -9
  201. package/src/hooks/useAddressAutocomplete.test.ts +18 -18
  202. package/src/hooks/useAppConfig.ts +2 -2
  203. package/src/hooks/useEventTheme.test.ts +7 -7
  204. package/src/hooks/useEventTheme.ts +2 -1
  205. package/src/hooks/useFileDisplay.ts +2 -2
  206. package/src/hooks/useFileUrl.ts +52 -8
  207. package/src/hooks/useOrganisationSecurity.test.ts +2 -1
  208. package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
  209. package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
  210. package/src/providers/__tests__/EventProvider.test.tsx +61 -61
  211. package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
  212. package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
  213. package/src/providers/__tests__/ProviderLifecycle.test.tsx +38 -38
  214. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
  215. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
  216. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
  217. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +15 -6
  218. package/src/rbac/__tests__/rbac-functions.test.ts +3 -3
  219. package/src/rbac/api.test.ts +104 -0
  220. package/src/rbac/engine.ts +1 -1
  221. package/src/rbac/hooks/useCan.test.ts +2 -2
  222. package/src/rbac/secureClient.ts +1 -1
  223. package/src/rbac/types/functions.ts +1 -1
  224. package/src/styles/core.css +7 -0
  225. package/src/theming/__tests__/parseEventColours.test.ts +118 -3
  226. package/src/theming/parseEventColours.ts +77 -11
  227. package/src/types/supabase.ts +2 -3
  228. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +9 -9
  229. package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
  230. package/src/utils/file-reference/__tests__/file-reference.test.ts +4 -0
  231. package/src/utils/formatting/formatDate.test.ts +3 -2
  232. package/src/utils/formatting/formatDateTime.test.ts +2 -2
  233. package/src/utils/google-places/googlePlacesUtils.test.ts +36 -24
  234. package/src/utils/storage/README.md +1 -1
  235. package/src/utils/storage/__tests__/helpers.unit.test.ts +19 -12
  236. package/src/utils/storage/helpers.test.ts +69 -3
  237. package/cursor-rules/01-standards-compliance.mdc +0 -285
  238. package/cursor-rules/04-testing-standards.mdc +0 -270
  239. package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
  240. package/cursor-rules/06-code-quality.mdc +0 -311
  241. package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
  242. package/cursor-rules/10-error-handling-patterns.mdc +0 -179
  243. package/cursor-rules/11-performance-optimization.mdc +0 -169
  244. package/cursor-rules/12-ci-cd-integration.mdc +0 -150
  245. package/dist/DataTable-LRJL4IRV.js +0 -15
  246. package/dist/eslint-rules/rules/compliance.cjs +0 -348
  247. package/dist/eslint-rules/rules/components.cjs +0 -113
  248. package/dist/eslint-rules/rules/imports.cjs +0 -102
  249. package/docs/best-practices/README.md +0 -472
  250. package/docs/best-practices/accessibility.md +0 -604
  251. package/docs/best-practices/common-patterns.md +0 -516
  252. package/docs/best-practices/deployment.md +0 -1103
  253. package/docs/best-practices/performance.md +0 -1328
  254. package/docs/best-practices/security.md +0 -940
  255. package/docs/best-practices/testing.md +0 -1034
  256. package/docs/rbac/compliance/compliance-guide.md +0 -544
  257. package/docs/standards/01-standards-compliance.md +0 -188
  258. package/docs/standards/03-solid-principles.md +0 -39
  259. package/docs/standards/04-testing-standards.md +0 -36
  260. package/docs/standards/05-bug-reports-and-features.md +0 -27
  261. package/docs/standards/06-code-quality.md +0 -34
  262. package/docs/standards/07-tech-stack-compliance.md +0 -30
  263. package/docs/standards/10-error-handling-patterns.md +0 -401
  264. package/docs/standards/11-performance-optimization.md +0 -348
  265. package/docs/standards/12-ci-cd-integration.md +0 -370
  266. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
  267. package/scripts/audit/audit-compliance.cjs +0 -1295
  268. package/scripts/audit/audit-components.cjs +0 -260
  269. package/scripts/audit/audit-rbac.cjs +0 -954
  270. package/scripts/audit/audit-standards.cjs +0 -1268
  271. package/scripts/audit/index.cjs +0 -1927
  272. package/src/components/DataTable/components/DataTableBody.tsx +0 -478
  273. package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
  274. package/src/components/DataTable/components/ExpandButton.tsx +0 -113
  275. package/src/components/DataTable/components/GroupHeader.tsx +0 -54
  276. package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
  277. package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
  278. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
  279. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
  280. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
  281. package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
  282. package/src/components/DataTable/core/DataTableContext.tsx +0 -216
  283. package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
  284. package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
  285. package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
  286. package/src/components/DataTable/utils/debugTools.ts +0 -514
  287. package/src/eslint-rules/index.cjs +0 -22
  288. package/src/eslint-rules/rules/components.cjs +0 -113
  289. package/src/eslint-rules/rules/imports.cjs +0 -102
  290. package/src/eslint-rules/rules/rbac.cjs +0 -790
  291. package/src/eslint-rules/utils/helpers.cjs +0 -42
  292. package/src/eslint-rules/utils/manifest-loader.cjs +0 -75
@@ -191,6 +191,14 @@ describe('RBAC API', () => {
191
191
 
192
192
  it('handles engine creation errors gracefully', () => {
193
193
  const error = new Error('Engine creation error');
194
+ // Ensure createRBACConfig doesn't throw so we can test engine creation error
195
+ mockCreateRBACConfig.mockImplementation((config) => config);
196
+ mockGetRBACLogger.mockReturnValue({
197
+ info: vi.fn(),
198
+ warn: vi.fn(),
199
+ error: vi.fn(),
200
+ debug: vi.fn()
201
+ });
194
202
  mockCreateRBACEngine.mockImplementation(() => {
195
203
  throw error;
196
204
  });
@@ -202,6 +210,16 @@ describe('RBAC API', () => {
202
210
 
203
211
  it('handles audit manager creation errors gracefully', () => {
204
212
  const error = new Error('Audit manager creation error');
213
+ // Ensure createRBACConfig doesn't throw so we can test audit manager creation error
214
+ mockCreateRBACConfig.mockImplementation((config) => config);
215
+ mockGetRBACLogger.mockReturnValue({
216
+ info: vi.fn(),
217
+ warn: vi.fn(),
218
+ error: vi.fn(),
219
+ debug: vi.fn()
220
+ });
221
+ const mockEngine = { id: 'test-engine' };
222
+ mockCreateRBACEngine.mockReturnValue(mockEngine as any);
205
223
  mockCreateAuditManager.mockImplementation(() => {
206
224
  throw error;
207
225
  });
@@ -309,6 +327,21 @@ describe('RBAC API', () => {
309
327
 
310
328
  describe('Error Handling', () => {
311
329
  it('handles missing supabase client', () => {
330
+ const mockEngine = { id: 'test-engine' };
331
+ const mockAuditManager = { id: 'test-audit' };
332
+ const mockLogger = {
333
+ info: vi.fn(),
334
+ warn: vi.fn(),
335
+ error: vi.fn(),
336
+ debug: vi.fn()
337
+ };
338
+
339
+ // Ensure mocks don't throw
340
+ mockCreateRBACConfig.mockImplementation((config) => config);
341
+ mockCreateRBACEngine.mockReturnValue(mockEngine as any);
342
+ mockCreateAuditManager.mockReturnValue(mockAuditManager as any);
343
+ mockGetRBACLogger.mockReturnValue(mockLogger);
344
+
312
345
  // The function doesn't throw, it just creates config with null
313
346
  expect(() => {
314
347
  setupRBAC(null as any);
@@ -317,6 +350,20 @@ describe('RBAC API', () => {
317
350
 
318
351
  it('handles invalid supabase client', () => {
319
352
  const invalidSupabase = { invalid: 'client' };
353
+ const mockEngine = { id: 'test-engine' };
354
+ const mockAuditManager = { id: 'test-audit' };
355
+ const mockLogger = {
356
+ info: vi.fn(),
357
+ warn: vi.fn(),
358
+ error: vi.fn(),
359
+ debug: vi.fn()
360
+ };
361
+
362
+ // Ensure mocks don't throw
363
+ mockCreateRBACConfig.mockImplementation((config) => config);
364
+ mockCreateRBACEngine.mockReturnValue(mockEngine as any);
365
+ mockCreateAuditManager.mockReturnValue(mockAuditManager as any);
366
+ mockGetRBACLogger.mockReturnValue(mockLogger);
320
367
 
321
368
  // The function doesn't throw, it just creates config with invalid client
322
369
  expect(() => {
@@ -354,6 +401,21 @@ describe('RBAC API', () => {
354
401
 
355
402
  describe('Configuration Validation', () => {
356
403
  it('validates required supabase client', () => {
404
+ const mockEngine = { id: 'test-engine' };
405
+ const mockAuditManager = { id: 'test-audit' };
406
+ const mockLogger = {
407
+ info: vi.fn(),
408
+ warn: vi.fn(),
409
+ error: vi.fn(),
410
+ debug: vi.fn()
411
+ };
412
+
413
+ // Ensure mocks don't throw
414
+ mockCreateRBACConfig.mockImplementation((config) => config);
415
+ mockCreateRBACEngine.mockReturnValue(mockEngine as any);
416
+ mockCreateAuditManager.mockReturnValue(mockAuditManager as any);
417
+ mockGetRBACLogger.mockReturnValue(mockLogger);
418
+
357
419
  // The function doesn't throw, it just creates config with undefined
358
420
  expect(() => {
359
421
  setupRBAC(undefined as any);
@@ -365,6 +427,20 @@ describe('RBAC API', () => {
365
427
  from: 'not-a-function',
366
428
  auth: 'not-an-object'
367
429
  };
430
+ const mockEngine = { id: 'test-engine' };
431
+ const mockAuditManager = { id: 'test-audit' };
432
+ const mockLogger = {
433
+ info: vi.fn(),
434
+ warn: vi.fn(),
435
+ error: vi.fn(),
436
+ debug: vi.fn()
437
+ };
438
+
439
+ // Ensure mocks don't throw
440
+ mockCreateRBACConfig.mockImplementation((config) => config);
441
+ mockCreateRBACEngine.mockReturnValue(mockEngine as any);
442
+ mockCreateAuditManager.mockReturnValue(mockAuditManager as any);
443
+ mockGetRBACLogger.mockReturnValue(mockLogger);
368
444
 
369
445
  // The function doesn't throw, it just creates config with invalid client
370
446
  expect(() => {
@@ -415,6 +491,32 @@ describe('RBAC API', () => {
415
491
  });
416
492
  });
417
493
 
494
+ describe('Type Safety', () => {
495
+ it('accepts valid Supabase client types', () => {
496
+ const validSupabase = {
497
+ from: vi.fn(),
498
+ auth: { getUser: vi.fn() },
499
+ rpc: vi.fn()
500
+ };
501
+ const mockEngine = { id: 'test-engine' };
502
+ const mockAuditManager = { id: 'test-audit' };
503
+ const mockLogger = {
504
+ info: vi.fn(),
505
+ warn: vi.fn(),
506
+ error: vi.fn(),
507
+ debug: vi.fn()
508
+ };
509
+
510
+ mockCreateRBACEngine.mockReturnValue(mockEngine as any);
511
+ mockCreateAuditManager.mockReturnValue(mockAuditManager as any);
512
+ mockGetRBACLogger.mockReturnValue(mockLogger);
513
+
514
+ expect(() => {
515
+ setupRBAC(validSupabase as any);
516
+ }).not.toThrow();
517
+ });
518
+ });
519
+
418
520
  describe('Cache Integration', () => {
419
521
  it('initializes cache correctly', () => {
420
522
  const mockEngine = { id: 'test-engine' };
@@ -456,6 +558,8 @@ describe('RBAC API', () => {
456
558
  debug: vi.fn()
457
559
  };
458
560
 
561
+ // Ensure mocks don't throw
562
+ mockCreateRBACConfig.mockImplementation((config) => config);
459
563
  mockCreateRBACEngine.mockReturnValue(mockEngine as any);
460
564
  mockCreateAuditManager.mockReturnValue(mockAuditManager as any);
461
565
  mockGetRBACLogger.mockReturnValue(mockLogger);
@@ -440,7 +440,7 @@ export class RBACEngine {
440
440
  async resolveAppContext(input: { userId: UUID; appName: string }): Promise<RBACAppContext | null> {
441
441
  try {
442
442
  const { userId, appName } = input;
443
- const { data, error } = await (this.supabase as any).rpc('util_app_resolve', {
443
+ const { data, error } = await (this.supabase as any).rpc('data_app_resolve', {
444
444
  p_user_id: userId,
445
445
  p_app_name: appName,
446
446
  });
@@ -647,7 +647,7 @@ describe('useCan Hook', () => {
647
647
  }, { timeout: 20000 });
648
648
  });
649
649
 
650
- it('times out after 3 seconds when organisationId is missing', async () => {
650
+ it('times out after 3 seconds when organisationId is missing', { timeout: 10000 }, async () => {
651
651
  vi.useFakeTimers({ shouldAdvanceTime: true });
652
652
 
653
653
  const emptyScope = {} as any;
@@ -672,7 +672,7 @@ describe('useCan Hook', () => {
672
672
  expect(result.current.error).not.toBeNull();
673
673
  expect(result.current.error?.message).toBe('Organisation context is required for permission checks');
674
674
  }, { timeout: 2000 });
675
- }, { timeout: 10000 });
675
+ });
676
676
 
677
677
  it('handles null userId', async () => {
678
678
  const { result } = renderHook(() =>
@@ -536,7 +536,7 @@ export class SecureSupabaseClient {
536
536
  // 2. Add it here with the parameters it accepts
537
537
  const rpcContextWhitelist: Record<string, Set<string>> = {
538
538
  // RPCs that accept all three context parameters
539
- 'rbac_roles_list': new Set(['p_organisation_id', 'p_event_id', 'p_app_id']),
539
+ 'data_rbac_roles_list': new Set(['p_organisation_id', 'p_event_id', 'p_app_id']),
540
540
 
541
541
  // RPCs that accept only p_organisation_id (not p_app_id or p_event_id)
542
542
  'data_file_reference_by_category_list': new Set(['p_organisation_id']),
@@ -185,7 +185,7 @@ export enum RPCFunction {
185
185
  // Role Management Functions
186
186
  RBAC_ROLE_GRANT = 'rbac_role_grant',
187
187
  RBAC_ROLE_REVOKE = 'rbac_role_revoke',
188
- RBAC_ROLES_LIST = 'rbac_roles_list',
188
+ RBAC_ROLES_LIST = 'data_rbac_roles_list',
189
189
  RBAC_ROLE_VALIDATE = 'rbac_role_validate',
190
190
 
191
191
  // Session & Audit Functions
@@ -206,6 +206,13 @@
206
206
  dialog::backdrop {
207
207
  background-color: oklch(from var(--color-main-700) l c h / 0.5);
208
208
  }
209
+
210
+ progress::indeterminate {
211
+
212
+ background: var(--color-acc-600);
213
+ /*
214
+ background: linear-gradient(to right, var(--color-acc-700) 10%, var(--color-sec-200) 30%, var(--color-main-500) 70%, var(--color-acc-200) 90%); */
215
+ }
209
216
 
210
217
  .appGradient {
211
218
  background: linear-gradient(145deg, var(--color-main-100) 10%, var(--color-main-200) 30%, var(--color-main-200) 70%, var(--color-main-300) 90%);
@@ -88,7 +88,7 @@ describe('parseAndNormalizeEventColours', () => {
88
88
  });
89
89
 
90
90
  describe('Standard format only', () => {
91
- it('returns null for legacy format with ev-main, ev-sec, ev-acc keys', () => {
91
+ it('supports ev-main, ev-sec, ev-acc keys and normalizes them', () => {
92
92
  const input = {
93
93
  'ev-main': { 500: { L: 0.5, C: 0.2, H: 0 } },
94
94
  'ev-sec': { 500: { L: 0.5, C: 0.2, H: 120 } },
@@ -96,8 +96,11 @@ describe('parseAndNormalizeEventColours', () => {
96
96
  };
97
97
 
98
98
  const result = parseAndNormalizeEventColours(input);
99
- // Legacy format is no longer supported - returns null
100
- expect(result).toBeNull();
99
+ // ev-main, ev-sec, ev-acc keys are supported and normalized to main, sec, acc
100
+ expect(result).not.toBeNull();
101
+ expect(result?.main[500]).toEqual({ L: 0.5, C: 0.2, H: 0 });
102
+ expect(result?.sec[500]).toEqual({ L: 0.5, C: 0.2, H: 120 });
103
+ expect(result?.acc[500]).toEqual({ L: 0.5, C: 0.2, H: 240 });
101
104
  });
102
105
 
103
106
  it('uses standard keys when provided', () => {
@@ -192,6 +195,118 @@ describe('parseAndNormalizeEventColours', () => {
192
195
  });
193
196
  });
194
197
 
198
+ describe('ev- prefix handling for shade names', () => {
199
+ it('strips ev- prefix from shade names', () => {
200
+ const input = {
201
+ main: {
202
+ 'ev-main-500': { L: 0.5, C: 0.2, H: 0 },
203
+ 'ev-main-100': { L: 0.9, C: 0.1, H: 0 }
204
+ },
205
+ acc: {
206
+ 'ev-acc-500': { L: 0.5, C: 0.2, H: 240 }
207
+ }
208
+ };
209
+
210
+ const result = parseAndNormalizeEventColours(input);
211
+ expect(result).not.toBeNull();
212
+
213
+ // ev- prefix should be stripped from shade names
214
+ expect(result?.main['main-500']).toEqual({ L: 0.5, C: 0.2, H: 0 });
215
+ expect(result?.main['main-100']).toEqual({ L: 0.9, C: 0.1, H: 0 });
216
+ expect(result?.acc['acc-500']).toEqual({ L: 0.5, C: 0.2, H: 240 });
217
+
218
+ // Original keys with ev- prefix should not exist
219
+ expect(result?.main['ev-main-500']).toBeUndefined();
220
+ expect(result?.main['ev-main-100']).toBeUndefined();
221
+ expect(result?.acc['ev-acc-500']).toBeUndefined();
222
+ });
223
+
224
+ it('handles mixed ev- prefixed and non-prefixed shade names', () => {
225
+ const input = {
226
+ main: {
227
+ 'ev-main-500': { L: 0.5, C: 0.2, H: 0 },
228
+ 100: { L: 0.9, C: 0.1, H: 0 },
229
+ raw: { L: 0.55, C: 0.25, H: 5 }
230
+ }
231
+ };
232
+
233
+ const result = parseAndNormalizeEventColours(input);
234
+ expect(result).not.toBeNull();
235
+
236
+ // ev- prefixed shade should be normalized
237
+ expect(result?.main['main-500']).toEqual({ L: 0.5, C: 0.2, H: 0 });
238
+
239
+ // Non-prefixed shades should remain unchanged
240
+ expect(result?.main[100]).toEqual({ L: 0.9, C: 0.1, H: 0 });
241
+ expect(result?.main.raw).toEqual({ L: 0.55, C: 0.25, H: 5 });
242
+ });
243
+
244
+ it('handles ev- prefixed shade names in sec palette', () => {
245
+ const input = {
246
+ sec: {
247
+ 'ev-sec-500': { L: 0.5, C: 0.2, H: 120 },
248
+ 'ev-sec-200': { L: 0.8, C: 0.15, H: 120 }
249
+ }
250
+ };
251
+
252
+ const result = parseAndNormalizeEventColours(input);
253
+ expect(result).not.toBeNull();
254
+
255
+ expect(result?.sec['sec-500']).toEqual({ L: 0.5, C: 0.2, H: 120 });
256
+ expect(result?.sec['sec-200']).toEqual({ L: 0.8, C: 0.15, H: 120 });
257
+ });
258
+
259
+ it('preserves shade names that do not start with ev-', () => {
260
+ const input = {
261
+ main: {
262
+ 500: { L: 0.5, C: 0.2, H: 0 },
263
+ 'main-500': { L: 0.6, C: 0.3, H: 0 },
264
+ raw: { L: 0.55, C: 0.25, H: 5 }
265
+ }
266
+ };
267
+
268
+ const result = parseAndNormalizeEventColours(input);
269
+ expect(result).not.toBeNull();
270
+
271
+ // All shades should be preserved as-is (no ev- prefix to strip)
272
+ expect(result?.main[500]).toEqual({ L: 0.5, C: 0.2, H: 0 });
273
+ expect(result?.main['main-500']).toEqual({ L: 0.6, C: 0.3, H: 0 });
274
+ expect(result?.main.raw).toEqual({ L: 0.55, C: 0.25, H: 5 });
275
+ });
276
+
277
+ it('normalizes both palette keys and shade names with ev- prefix together', () => {
278
+ const input = {
279
+ 'ev-main': {
280
+ 'ev-main-raw': { L: 0.55, C: 0.25, H: 5 },
281
+ 'ev-main-200': { L: 0.8, C: 0.15, H: 0 }
282
+ },
283
+ 'ev-sec': {
284
+ 'ev-sec-200': { L: 0.8, C: 0.15, H: 120 },
285
+ 'ev-sec-500': { L: 0.5, C: 0.2, H: 120 }
286
+ },
287
+ 'ev-acc': {
288
+ 'ev-acc-800': { L: 0.3, C: 0.2, H: 240 }
289
+ }
290
+ };
291
+
292
+ const result = parseAndNormalizeEventColours(input);
293
+ expect(result).not.toBeNull();
294
+
295
+ // Palette keys should be normalized: ev-main -> main, ev-sec -> sec, ev-acc -> acc
296
+ // Shade names should be normalized: ev-main-raw -> main-raw, ev-sec-200 -> sec-200, etc.
297
+ expect(result?.main['main-raw']).toEqual({ L: 0.55, C: 0.25, H: 5 });
298
+ expect(result?.main['main-200']).toEqual({ L: 0.8, C: 0.15, H: 0 });
299
+ expect(result?.sec['sec-200']).toEqual({ L: 0.8, C: 0.15, H: 120 });
300
+ expect(result?.sec['sec-500']).toEqual({ L: 0.5, C: 0.2, H: 120 });
301
+ expect(result?.acc['acc-800']).toEqual({ L: 0.3, C: 0.2, H: 240 });
302
+
303
+ // Original keys should not exist
304
+ expect(result?.main['ev-main-raw']).toBeUndefined();
305
+ expect(result?.sec['ev-sec-200']).toBeUndefined();
306
+ expect(result?.acc['ev-acc-800']).toBeUndefined();
307
+ });
308
+ });
309
+
195
310
  describe('Error handling', () => {
196
311
  it('handles malformed color objects gracefully', () => {
197
312
  const input = {
@@ -8,21 +8,28 @@
8
8
  * Handles multiple input formats and ensures consistent palette structure.
9
9
  */
10
10
 
11
- import { createLogger } from '../utils/core/logger';
12
-
13
- const log = createLogger('ParseEventColours');
14
11
 
15
12
  /**
16
13
  * Parse and normalize event_colours to PaletteData
17
14
  *
18
15
  * Supports input formats:
19
- * - Object with 'main', 'sec', 'acc' keys
16
+ * - Object with 'main', 'sec', 'acc' keys (standard format)
17
+ * - Object with 'ev-main', 'ev-sec', 'ev-acc' keys (database format with prefix)
18
+ * - Shade names with 'ev-' prefix (e.g., 'ev-acc-500' -> 'acc-500', 'ev-main-raw' -> 'main-raw')
20
19
  * - JSON string that will be parsed
21
20
  *
22
21
  * Only includes explicitly defined color values. Does not fill
23
22
  * missing shades - only shades that are present in the input will
24
23
  * be included in the output.
25
24
  *
25
+ * The parser automatically strips 'ev-' prefixes from BOTH palette keys and shade names
26
+ * for future-proofing, allowing distinction between event colors and organization colors
27
+ * (org colors not yet implemented).
28
+ *
29
+ * Normalization rules:
30
+ * - Palette keys: 'ev-main' -> 'main', 'ev-sec' -> 'sec', 'ev-acc' -> 'acc'
31
+ * - Shade names: 'ev-main-raw' -> 'main-raw', 'ev-sec-200' -> 'sec-200', 'ev-acc-800' -> 'acc-800'
32
+ *
26
33
  * @param input - Event colours from database (JSONB field)
27
34
  * @returns Normalized palette data with main, sec, acc palettes, or null if invalid
28
35
  *
@@ -38,6 +45,56 @@ const log = createLogger('ParseEventColours');
38
45
  * // Returns: { main: { 500: {...}, raw: {...} }, sec: { 500: {...} }, acc: { 500: {...} } }
39
46
  * ```
40
47
  *
48
+ * @example
49
+ * ```ts
50
+ * // Database format with ev- prefix on palette keys
51
+ * const colours = {
52
+ * 'ev-main': { 500: { L: 0.5, C: 0.2, H: 0 } },
53
+ * 'ev-sec': { 500: { L: 0.5, C: 0.2, H: 120 } },
54
+ * 'ev-acc': { 500: { L: 0.5, C: 0.2, H: 240 } }
55
+ * };
56
+ * const palette = parseAndNormalizeEventColours(colours);
57
+ * // Returns: { main: { 500: {...} }, sec: { 500: {...} }, acc: { 500: {...} } }
58
+ * ```
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * // Future-proofing: ev- prefix on shade names
63
+ * const colours = {
64
+ * main: {
65
+ * 'ev-main-500': { L: 0.5, C: 0.2, H: 0 },
66
+ * 'ev-main-raw': { L: 0.55, C: 0.25, H: 5 }
67
+ * },
68
+ * sec: {
69
+ * 'ev-sec-200': { L: 0.8, C: 0.15, H: 120 }
70
+ * },
71
+ * acc: {
72
+ * 'ev-acc-800': { L: 0.3, C: 0.2, H: 240 }
73
+ * }
74
+ * };
75
+ * const palette = parseAndNormalizeEventColours(colours);
76
+ * // Returns: { main: { 'main-500': {...}, 'main-raw': {...} }, sec: { 'sec-200': {...} }, acc: { 'acc-800': {...} } }
77
+ * ```
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * // Both palette keys and shade names with ev- prefix
82
+ * const colours = {
83
+ * 'ev-main': {
84
+ * 'ev-main-raw': { L: 0.55, C: 0.25, H: 5 },
85
+ * 'ev-main-200': { L: 0.8, C: 0.15, H: 0 }
86
+ * },
87
+ * 'ev-sec': {
88
+ * 'ev-sec-500': { L: 0.5, C: 0.2, H: 120 }
89
+ * },
90
+ * 'ev-acc': {
91
+ * 'ev-acc-800': { L: 0.3, C: 0.2, H: 240 }
92
+ * }
93
+ * };
94
+ * const palette = parseAndNormalizeEventColours(colours);
95
+ * // Returns: { main: { 'main-raw': {...}, 'main-200': {...} }, sec: { 'sec-500': {...} }, acc: { 'acc-800': {...} } }
96
+ * ```
97
+ *
41
98
  */
42
99
  export function parseAndNormalizeEventColours(input: unknown): { main: any; sec: any; acc: any } | null {
43
100
  try {
@@ -55,16 +112,21 @@ export function parseAndNormalizeEventColours(input: unknown): { main: any; sec:
55
112
  return null;
56
113
  }
57
114
 
58
- // Use standard 'main'/'sec'/'acc' keys
59
- const main = obj?.main || null;
60
- const sec = obj?.sec || null;
61
- const acc = obj?.acc || null;
115
+ // Support both standard keys (main/sec/acc) and prefixed keys (ev-main/ev-sec/ev-acc)
116
+ // Database may store with ev-* prefix, but we normalize to standard keys
117
+ const main = obj?.main || obj?.['ev-main'] || null;
118
+ const sec = obj?.sec || obj?.['ev-sec'] || null;
119
+ const acc = obj?.acc || obj?.['ev-acc'] || null;
62
120
 
63
121
  // If no palette data found, return null
64
- if (!main && !sec && !acc) return null;
122
+ if (!main && !sec && !acc) {
123
+ return null;
124
+ }
65
125
 
66
126
  // Helper: only include explicitly defined color values
67
127
  // This ensures we don't include undefined shades in the palette
128
+ // Also strips 'ev-' prefix from shade names for future-proofing (to distinguish event colors from org colors)
129
+ // Palette keys are normalized above (ev-main -> main), shade names are normalized here (ev-main-raw -> main-raw)
68
130
  const fill = (p: any) => {
69
131
  if (!p || typeof p !== 'object') return {} as any;
70
132
  const out: any = {};
@@ -75,7 +137,12 @@ export function parseAndNormalizeEventColours(input: unknown): { main: any; sec:
75
137
  // Only include the key if it has a defined value (not null, undefined, or empty)
76
138
  const value = p[key];
77
139
  if (value !== null && value !== undefined && value !== '') {
78
- out[key] = value;
140
+ // Strip 'ev-' prefix from shade names for future-proofing
141
+ // e.g., 'ev-acc-500' -> 'acc-500', 'ev-main-raw' -> 'main-raw', 'ev-sec-200' -> 'sec-200'
142
+ const normalizedKey = typeof key === 'string' && key.startsWith('ev-')
143
+ ? key.substring(3)
144
+ : key;
145
+ out[normalizedKey] = value;
79
146
  }
80
147
  }
81
148
 
@@ -88,7 +155,6 @@ export function parseAndNormalizeEventColours(input: unknown): { main: any; sec:
88
155
  acc: fill(acc)
89
156
  };
90
157
  } catch (error) {
91
- log.warn('Failed to parse/normalize event colours:', error);
92
158
  return null;
93
159
  }
94
160
  }
@@ -128,7 +128,7 @@ export type RPCFunction =
128
128
  | 'rbac_page_access_check'
129
129
  | 'rbac_role_grant'
130
130
  | 'rbac_role_revoke'
131
- | 'rbac_roles_list'
131
+ | 'data_rbac_roles_list'
132
132
  | 'rbac_role_validate'
133
133
  | 'rbac_session_track'
134
134
  | 'rbac_audit_log'
@@ -137,8 +137,7 @@ export type RPCFunction =
137
137
  | 'data_user_organisation_roles_get'
138
138
  | 'data_user_organisations_get'
139
139
  | 'data_cake_meals_get'
140
- // Utility Functions
141
- | 'util_app_resolve'
140
+ | 'data_app_resolve'
142
141
  | 'debug_auth_context'
143
142
  | 'handle_new_user'
144
143
  // Other functions
@@ -75,8 +75,8 @@ describe('bundleAnalysis', () => {
75
75
 
76
76
  describe('bundleAnalyzer.checkTreeshaking', () => {
77
77
  it('should do nothing in production mode', async () => {
78
- const originalEnv = process.env.NODE_ENV;
79
- process.env.NODE_ENV = 'production';
78
+ // Mock import.meta.env.MODE for production using vi.stubEnv
79
+ vi.stubEnv('MODE', 'production');
80
80
 
81
81
  vi.resetModules();
82
82
  const { bundleAnalyzer } = await import('../performance/bundleAnalysis');
@@ -84,7 +84,7 @@ describe('bundleAnalysis', () => {
84
84
 
85
85
  expect(consoleSpy.group).not.toHaveBeenCalled();
86
86
 
87
- process.env.NODE_ENV = originalEnv;
87
+ vi.unstubAllEnvs();
88
88
  });
89
89
 
90
90
  it('should log treeshaking analysis in development mode', async () => {
@@ -115,8 +115,8 @@ describe('bundleAnalysis', () => {
115
115
 
116
116
  describe('bundleAnalyzer.validateExports', () => {
117
117
  it('should do nothing in production mode', async () => {
118
- const originalEnv = process.env.NODE_ENV;
119
- process.env.NODE_ENV = 'production';
118
+ // Mock import.meta.env.MODE for production using vi.stubEnv
119
+ vi.stubEnv('MODE', 'production');
120
120
 
121
121
  vi.resetModules();
122
122
  const { bundleAnalyzer } = await import('../performance/bundleAnalysis');
@@ -124,7 +124,7 @@ describe('bundleAnalysis', () => {
124
124
 
125
125
  expect(consoleSpy.group).not.toHaveBeenCalled();
126
126
 
127
- process.env.NODE_ENV = originalEnv;
127
+ vi.unstubAllEnvs();
128
128
  });
129
129
 
130
130
  it('should log export validation in development mode', async () => {
@@ -290,8 +290,8 @@ describe('bundleAnalysis', () => {
290
290
 
291
291
  describe('trackDynamicImport', () => {
292
292
  it('should do nothing in production mode', async () => {
293
- const originalEnv = process.env.NODE_ENV;
294
- process.env.NODE_ENV = 'production';
293
+ // Mock import.meta.env.MODE for production using vi.stubEnv
294
+ vi.stubEnv('MODE', 'production');
295
295
 
296
296
  vi.resetModules();
297
297
  const { trackDynamicImport } = await import('../performance/bundleAnalysis');
@@ -299,7 +299,7 @@ describe('bundleAnalysis', () => {
299
299
 
300
300
  expect(consoleSpy.log).not.toHaveBeenCalled();
301
301
 
302
- process.env.NODE_ENV = originalEnv;
302
+ vi.unstubAllEnvs();
303
303
  });
304
304
 
305
305
  it('should log dynamic imports in development mode', async () => {