@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
@@ -0,0 +1,349 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Installation Script for pace-core ESLint Config
5
+ * @package @jmruthers/pace-core
6
+ * @module Scripts/install-eslint-config
7
+ *
8
+ * Sets up pace-core ESLint configuration in consuming apps.
9
+ * Adds pace-core ESLint config to consuming app's ESLint configuration.
10
+ *
11
+ * This is an opt-in script - it does NOT run automatically via postinstall.
12
+ *
13
+ * Usage:
14
+ * node install-eslint-config.cjs # Setup ESLint config
15
+ * node install-eslint-config.cjs --force # Force update even if already configured
16
+ */
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+
21
+ // Check for safety guards
22
+ function checkSafetyGuards() {
23
+ // Check environment variable
24
+ if (process.env.PACE_ESLINT_CONFIG_DISABLED === '1') {
25
+ console.log(`${colors.yellow}Skipping ESLint config installation: PACE_ESLINT_CONFIG_DISABLED=1${colors.reset}`);
26
+ process.exit(0);
27
+ }
28
+
29
+ // Check for .git folder (safety guard for CI)
30
+ const cwd = process.cwd();
31
+ if (!fs.existsSync(path.join(cwd, '.git'))) {
32
+ console.log(`${colors.yellow}Skipping ESLint config installation: No .git folder found${colors.reset}`);
33
+ console.log(`${colors.yellow}This is a safety guard. If you want to install anyway, run with --force${colors.reset}`);
34
+ if (!process.argv.includes('--force')) {
35
+ process.exit(0);
36
+ }
37
+ }
38
+ }
39
+
40
+ // ANSI color codes for terminal output
41
+ const colors = {
42
+ reset: '\x1b[0m',
43
+ green: '\x1b[32m',
44
+ yellow: '\x1b[33m',
45
+ blue: '\x1b[34m',
46
+ cyan: '\x1b[36m',
47
+ bold: '\x1b[1m',
48
+ red: '\x1b[31m'
49
+ };
50
+
51
+ // Find existing ESLint config file
52
+ function findESLintConfig() {
53
+ const cwd = process.cwd();
54
+ const possibleConfigs = [
55
+ 'eslint.config.js',
56
+ 'eslint.config.cjs',
57
+ 'eslint.config.mjs',
58
+ '.eslintrc.js',
59
+ '.eslintrc.cjs',
60
+ '.eslintrc.json',
61
+ '.eslintrc.yaml',
62
+ '.eslintrc.yml',
63
+ ];
64
+
65
+ for (const configFile of possibleConfigs) {
66
+ const configPath = path.join(cwd, configFile);
67
+ if (fs.existsSync(configPath)) {
68
+ return { path: configPath, name: configFile, isESM: configFile.endsWith('.js') || configFile.endsWith('.mjs') };
69
+ }
70
+ }
71
+
72
+ return null;
73
+ }
74
+
75
+ // Check if pace-core config is already included
76
+ function hasPaceCoreConfig(configContent) {
77
+ // Check for pace-core config import/require
78
+ const paceCorePatterns = [
79
+ /@jmruthers\/pace-core\/eslint-config/,
80
+ /pace-core\/eslint-config/,
81
+ /paceCoreConfig/,
82
+ /pace-core-compliance/,
83
+ ];
84
+
85
+ return paceCorePatterns.some(pattern => pattern.test(configContent));
86
+ }
87
+
88
+ // Detect if file is ES module
89
+ function isESModule(filePath, content) {
90
+ if (filePath.endsWith('.mjs')) return true;
91
+ if (filePath.endsWith('.cjs')) return false;
92
+ if (filePath.endsWith('.js')) {
93
+ // Check for ES module indicators
94
+ return /^import\s+.*from|^export\s+default/.test(content.trim());
95
+ }
96
+ return false;
97
+ }
98
+
99
+ // Detect if config uses tseslint.config() wrapper
100
+ function usesTseslintConfig(content) {
101
+ return /tseslint\.config\s*\(/.test(content);
102
+ }
103
+
104
+ // Validate ESLint config structure
105
+ function validateConfig(configPath, content) {
106
+ try {
107
+ // Basic validation - check for common syntax errors
108
+ // Check for balanced brackets
109
+ const openBrackets = (content.match(/\[/g) || []).length;
110
+ const closeBrackets = (content.match(/\]/g) || []).length;
111
+ if (openBrackets !== closeBrackets) {
112
+ return { valid: false, error: `Unbalanced brackets: ${openBrackets} open, ${closeBrackets} close` };
113
+ }
114
+
115
+ // Check for balanced parentheses
116
+ const openParens = (content.match(/\(/g) || []).length;
117
+ const closeParens = (content.match(/\)/g) || []).length;
118
+ if (openParens !== closeParens) {
119
+ return { valid: false, error: `Unbalanced parentheses: ${openParens} open, ${closeParens} close` };
120
+ }
121
+
122
+ // Check that paceCoreConfig is spread correctly
123
+ if (content.includes('...paceCoreConfig')) {
124
+ // Should be spread in an array or tseslint.config()
125
+ if (!content.includes('[...paceCoreConfig') && !content.includes('tseslint.config(...paceCoreConfig')) {
126
+ return { valid: false, error: 'paceCoreConfig must be spread in an array or tseslint.config()' };
127
+ }
128
+ }
129
+
130
+ return { valid: true };
131
+ } catch (error) {
132
+ return { valid: false, error: error.message };
133
+ }
134
+ }
135
+
136
+ // Backup file before modification
137
+ function backupFile(filePath) {
138
+ const backupPath = `${filePath}.backup.${Date.now()}`;
139
+ fs.copyFileSync(filePath, backupPath);
140
+ return backupPath;
141
+ }
142
+
143
+ // Setup ESLint configuration
144
+ function setupESLintConfig(force = false) {
145
+ const cwd = process.cwd();
146
+ const existingConfig = findESLintConfig();
147
+
148
+ if (existingConfig) {
149
+ // Read existing config
150
+ let content = fs.readFileSync(existingConfig.path, 'utf8');
151
+ const isESM = isESModule(existingConfig.path, content);
152
+ const format = isESM ? 'ES Module' : 'CommonJS';
153
+
154
+ console.log(`${colors.cyan}Found ESLint config:${colors.reset} ${existingConfig.name} (${format})`);
155
+
156
+ // Check if already configured
157
+ if (hasPaceCoreConfig(content)) {
158
+ if (!force) {
159
+ console.log(`${colors.blue}○${colors.reset} ${existingConfig.name} already includes pace-core rules`);
160
+ console.log(`${colors.blue} Use --force to update.${colors.reset}`);
161
+ return { action: 'skipped', file: existingConfig.name, format };
162
+ }
163
+
164
+ console.log(`${colors.yellow}Updating existing ESLint config...${colors.reset}`);
165
+ } else {
166
+ console.log(`${colors.cyan}Adding pace-core config to ${existingConfig.name}...${colors.reset}`);
167
+ }
168
+
169
+ // Backup before modification
170
+ const backupPath = backupFile(existingConfig.path);
171
+ console.log(`${colors.cyan} Backed up to ${path.basename(backupPath)}${colors.reset}`);
172
+
173
+ // Add pace-core config
174
+ if (isESM) {
175
+ // ES Module format
176
+ if (!content.includes('import paceCoreConfig')) {
177
+ // Add import at top (after other imports if they exist)
178
+ const importLines = content.match(/^(import\s+[^;]+;?\s*\n)+/m);
179
+ if (importLines) {
180
+ // Add after existing imports
181
+ content = content.replace(
182
+ importLines[0],
183
+ `${importLines[0]}import paceCoreConfig from '@jmruthers/pace-core/eslint-config';\n`
184
+ );
185
+ } else {
186
+ // Add at the beginning
187
+ content = `import paceCoreConfig from '@jmruthers/pace-core/eslint-config';\n${content}`;
188
+ }
189
+ }
190
+
191
+ // Handle tseslint.config() wrapper
192
+ if (usesTseslintConfig(content)) {
193
+ // Config uses tseslint.config() - spread paceCoreConfig as first argument
194
+ if (!content.includes('...paceCoreConfig')) {
195
+ // Find tseslint.config( and add paceCoreConfig as first argument
196
+ // Handle both cases: tseslint.config(...) and tseslint.config(\n...)
197
+ if (content.match(/tseslint\.config\s*\(\s*[^\n]/)) {
198
+ // Has content on same line, add after opening paren
199
+ content = content.replace(
200
+ /(tseslint\.config\s*\()\s*/,
201
+ '$1\n ...paceCoreConfig,\n '
202
+ );
203
+ } else {
204
+ // Has content on new line, add as first line
205
+ content = content.replace(
206
+ /(tseslint\.config\s*\(\s*\n)\s*/,
207
+ '$1 ...paceCoreConfig,\n '
208
+ );
209
+ }
210
+ }
211
+ } else if (content.includes('export default [')) {
212
+ // Already an array, add paceCoreConfig at the beginning
213
+ if (!content.includes('...paceCoreConfig')) {
214
+ content = content.replace(
215
+ /(export\s+default\s*\[)\s*/,
216
+ '$1\n ...paceCoreConfig,'
217
+ );
218
+ }
219
+ } else if (content.includes('export default')) {
220
+ // Not an array, wrap it
221
+ const exportMatch = content.match(/(export\s+default\s+)(.+?)(;?\s*$)/s);
222
+ if (exportMatch) {
223
+ content = content.replace(
224
+ exportMatch[0],
225
+ `${exportMatch[1]}[\n ...paceCoreConfig,\n ${exportMatch[2]}\n];`
226
+ );
227
+ }
228
+ }
229
+ } else {
230
+ // CommonJS format
231
+ if (!content.includes('require(\'@jmruthers/pace-core/eslint-config\')')) {
232
+ // Add require at top (after other requires if they exist)
233
+ const requireLines = content.match(/^(const\s+\w+\s*=\s*require\([^)]+\);\s*\n)+/m);
234
+ if (requireLines) {
235
+ content = content.replace(
236
+ requireLines[0],
237
+ `${requireLines[0]}const paceCoreConfig = require('@jmruthers/pace-core/eslint-config');\n`
238
+ );
239
+ } else {
240
+ content = `const paceCoreConfig = require('@jmruthers/pace-core/eslint-config');\n${content}`;
241
+ }
242
+ }
243
+
244
+ // Add to module.exports
245
+ if (content.includes('module.exports = [')) {
246
+ // Already an array, add paceCoreConfig at the beginning
247
+ if (!content.includes('...paceCoreConfig')) {
248
+ content = content.replace(
249
+ /(module\.exports\s*=\s*\[)\s*/,
250
+ '$1\n ...paceCoreConfig,'
251
+ );
252
+ }
253
+ } else if (content.includes('module.exports =')) {
254
+ // Not an array, wrap it
255
+ const exportMatch = content.match(/(module\.exports\s*=\s*)(.+?)(;?\s*$)/s);
256
+ if (exportMatch) {
257
+ content = content.replace(
258
+ exportMatch[0],
259
+ `${exportMatch[1]}[\n ...paceCoreConfig,\n ${exportMatch[2]}\n];`
260
+ );
261
+ }
262
+ }
263
+ }
264
+
265
+ // Validate config before writing
266
+ const validation = validateConfig(existingConfig.path, content);
267
+ if (!validation.valid) {
268
+ console.error(`${colors.red}✗${colors.reset} Config validation failed: ${validation.error}`);
269
+ console.error(`${colors.yellow} Restoring backup...${colors.reset}`);
270
+ fs.copyFileSync(backupPath, existingConfig.path);
271
+ throw new Error(`Invalid ESLint config structure: ${validation.error}`);
272
+ }
273
+
274
+ // Write updated config
275
+ fs.writeFileSync(existingConfig.path, content, 'utf8');
276
+ console.log(`${colors.green}✓${colors.reset} Updated ${existingConfig.name}`);
277
+ return { action: 'updated', file: existingConfig.name, backup: backupPath, format };
278
+ } else {
279
+ // Create new ESLint config (default to ES modules)
280
+ const configPath = path.join(cwd, 'eslint.config.js');
281
+ const configContent = `import paceCoreConfig from '@jmruthers/pace-core/eslint-config';
282
+
283
+ export default [
284
+ ...paceCoreConfig,
285
+ // Your app-specific ESLint configuration
286
+ {
287
+ // Add your rules here
288
+ },
289
+ ];
290
+ `;
291
+
292
+ console.log(`${colors.cyan}No ESLint config found. Creating eslint.config.js...${colors.reset}`);
293
+ fs.writeFileSync(configPath, configContent, 'utf8');
294
+ console.log(`${colors.green}✓${colors.reset} Created eslint.config.js`);
295
+ console.log(`${colors.cyan} Edit eslint.config.js to add your app-specific ESLint rules.${colors.reset}`);
296
+ return { action: 'created', file: 'eslint.config.js', format: 'ES Module' };
297
+ }
298
+ }
299
+
300
+ // Main execution
301
+ function main() {
302
+ checkSafetyGuards();
303
+
304
+ const force = process.argv.includes('--force');
305
+
306
+ if (force) {
307
+ console.log(`${colors.yellow}Warning: --force flag is set. This will overwrite existing configurations.${colors.reset}\n`);
308
+ }
309
+
310
+ try {
311
+ console.log(`${colors.cyan}Setting up ESLint configuration from pace-core...${colors.reset}\n`);
312
+ const result = setupESLintConfig(force);
313
+
314
+ // Summary
315
+ console.log(`\n${colors.bold}Setup Summary:${colors.reset}`);
316
+ if (result.action === 'created') {
317
+ console.log(` ${colors.green}ESLint Config:${colors.reset} Created ${result.file} (${result.format})`);
318
+ } else if (result.action === 'updated') {
319
+ console.log(` ${colors.green}ESLint Config:${colors.reset} Updated ${result.file} (${result.format})`);
320
+ console.log(` ${colors.yellow}Backup:${colors.reset} ${path.basename(result.backup)}`);
321
+ } else {
322
+ console.log(` ${colors.blue}ESLint Config:${colors.reset} Already configured (${result.file}, ${result.format})`);
323
+ }
324
+
325
+ console.log(`\n${colors.cyan}ESLint configuration is now available${colors.reset}`);
326
+ console.log(`\n${colors.cyan}Next steps:${colors.reset}`);
327
+ console.log(` • Run ${colors.bold}npm run lint${colors.reset} to verify ESLint is working`);
328
+ console.log(` • Edit your ESLint config to add app-specific rules`);
329
+ console.log(` • See ${colors.bold}packages/core/docs/standards/${colors.reset} for complete standards documentation`);
330
+
331
+ } catch (error) {
332
+ console.error(`${colors.red}Error during setup:${colors.reset}`);
333
+ console.error(error.message);
334
+ if (error.stack) {
335
+ console.error(error.stack);
336
+ }
337
+ process.exit(1);
338
+ }
339
+ }
340
+
341
+ // Run if called directly
342
+ if (require.main === module) {
343
+ main();
344
+ }
345
+
346
+ module.exports = {
347
+ setupESLintConfig,
348
+ findESLintConfig
349
+ };
@@ -0,0 +1,248 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Dependency Validation Script
5
+ * @package @jmruthers/pace-core
6
+ * @module Scripts/ValidateDependencies
7
+ *
8
+ * Validates consistency between package.json, audit tool, and documentation.
9
+ * Ensures package.json is the single source of truth for dependencies.
10
+ *
11
+ * Checks:
12
+ * - All peerDependencies have corresponding entries in peerDependenciesMeta
13
+ * - No peer dependency is also in dependencies
14
+ * - Required peers (not optional) are correctly marked
15
+ * - Audit tool can read package.json correctly
16
+ * - No hardcoded dependency lists exist in audit tool
17
+ *
18
+ * Usage:
19
+ * node scripts/validate-dependencies.cjs
20
+ * npm run validate:dependencies
21
+ */
22
+
23
+ const fs = require('fs');
24
+ const path = require('path');
25
+
26
+ // Colors for terminal output
27
+ const colors = {
28
+ reset: '\x1b[0m',
29
+ red: '\x1b[31m',
30
+ green: '\x1b[32m',
31
+ yellow: '\x1b[33m',
32
+ blue: '\x1b[34m',
33
+ cyan: '\x1b[36m',
34
+ bold: '\x1b[1m',
35
+ };
36
+
37
+ // Get package.json path
38
+ const packageJsonPath = path.resolve(__dirname, '../package.json');
39
+ const auditToolPath = path.resolve(__dirname, '../audit-tool/00-dependencies.cjs');
40
+
41
+ let hasErrors = false;
42
+ let hasWarnings = false;
43
+
44
+ /**
45
+ * Check package.json structure
46
+ */
47
+ function validatePackageJson() {
48
+ console.log(`${colors.blue}Validating package.json structure...${colors.reset}`);
49
+
50
+ if (!fs.existsSync(packageJsonPath)) {
51
+ console.error(`${colors.red}Error: package.json not found at ${packageJsonPath}${colors.reset}`);
52
+ return false;
53
+ }
54
+
55
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
56
+ const peerDeps = pkg.peerDependencies || {};
57
+ const peerMeta = pkg.peerDependenciesMeta || {};
58
+ const dependencies = pkg.dependencies || {};
59
+
60
+ let valid = true;
61
+
62
+ // Check 1: All OPTIONAL peerDependencies must have entries in peerDependenciesMeta
63
+ // Required peers (not optional) should NOT be in peerDependenciesMeta
64
+ // Only optional peers need to be explicitly marked
65
+ const optionalPeersInMeta = Object.keys(peerMeta).filter(dep => peerMeta[dep]?.optional);
66
+ const optionalPeersNotInMeta = Object.keys(peerDeps).filter(dep => {
67
+ // If it's in dependencies, it shouldn't be a peer at all (handled by check 2)
68
+ if (dependencies[dep]) return false;
69
+ // If it's marked as optional in meta, it's fine
70
+ if (peerMeta[dep]?.optional) return false;
71
+ // If it's not in meta and not in dependencies, we need to check if it should be optional
72
+ // For now, we'll only warn if a peer is in meta but not marked as optional
73
+ return false;
74
+ });
75
+
76
+ // Check for peers that are marked as optional in meta but should be required
77
+ // This is a warning, not an error, as the audit tool will treat them correctly
78
+ const requiredPeersInMeta = Object.keys(peerMeta).filter(
79
+ dep => peerDeps[dep] && !peerMeta[dep]?.optional
80
+ );
81
+ if (requiredPeersInMeta.length > 0) {
82
+ console.warn(`${colors.yellow}⚠️ Warning: The following required peers are in peerDependenciesMeta (they don't need to be):${colors.reset}`);
83
+ requiredPeersInMeta.forEach(dep => {
84
+ console.warn(` - ${colors.yellow}${dep}${colors.reset}`);
85
+ });
86
+ console.warn(`${colors.yellow} → Required peers don't need to be in peerDependenciesMeta (only optional ones do)${colors.reset}`);
87
+ hasWarnings = true;
88
+ }
89
+
90
+ // Check 2: No peer dependency is also in dependencies
91
+ const duplicateDeps = Object.keys(peerDeps).filter(dep => dependencies[dep]);
92
+ if (duplicateDeps.length > 0) {
93
+ console.error(`${colors.red}❌ Error: The following packages appear in both peerDependencies and dependencies:${colors.reset}`);
94
+ duplicateDeps.forEach(dep => {
95
+ console.error(` - ${colors.red}${dep}${colors.reset}`);
96
+ });
97
+ console.error(`${colors.yellow} → Remove them from peerDependencies (they're already included)${colors.reset}`);
98
+ valid = false;
99
+ hasErrors = true;
100
+ }
101
+
102
+ // Check 3: Verify no required peers are marked as optional
103
+ // Required peers are those NOT in peerDependenciesMeta (or marked as optional: false)
104
+ const requiredPeers = Object.keys(peerDeps).filter(dep => {
105
+ // If it's in dependencies, it's not a peer (handled by check 2)
106
+ if (dependencies[dep]) return false;
107
+ // If it's not in meta, it's required by default
108
+ if (!peerMeta[dep]) return true;
109
+ // If it's in meta and marked as optional: false, it's required
110
+ if (peerMeta[dep]?.optional === false) return true;
111
+ // If it's in meta and marked as optional: true, it's optional
112
+ return false;
113
+ });
114
+
115
+ const incorrectlyOptional = requiredPeers.filter(dep => peerMeta[dep]?.optional === true);
116
+ if (incorrectlyOptional.length > 0) {
117
+ console.error(`${colors.red}❌ Error: The following required peers are incorrectly marked as optional:${colors.reset}`);
118
+ incorrectlyOptional.forEach(dep => {
119
+ console.error(` - ${colors.red}${dep}${colors.reset}`);
120
+ });
121
+ console.error(`${colors.yellow} → Remove "optional": true from peerDependenciesMeta for these packages (or remove them from meta entirely)${colors.reset}`);
122
+ valid = false;
123
+ hasErrors = true;
124
+ }
125
+
126
+ // Check 4: Warn about extra entries in peerDependenciesMeta that don't exist in peerDependencies
127
+ const extraMeta = Object.keys(peerMeta).filter(dep => !peerDeps[dep]);
128
+ if (extraMeta.length > 0) {
129
+ console.warn(`${colors.yellow}⚠️ Warning: The following entries in peerDependenciesMeta don't exist in peerDependencies:${colors.reset}`);
130
+ extraMeta.forEach(dep => {
131
+ console.warn(` - ${colors.yellow}${dep}${colors.reset}`);
132
+ });
133
+ console.warn(`${colors.yellow} → Remove them from peerDependenciesMeta or add them to peerDependencies${colors.reset}`);
134
+ hasWarnings = true;
135
+ }
136
+
137
+ if (valid && duplicateDeps.length === 0) {
138
+ console.log(`${colors.green}✅ package.json structure is valid${colors.reset}`);
139
+ }
140
+
141
+ return valid;
142
+ }
143
+
144
+ /**
145
+ * Check audit tool consistency
146
+ */
147
+ function validateAuditTool() {
148
+ console.log(`${colors.blue}Validating audit tool consistency...${colors.reset}`);
149
+
150
+ if (!fs.existsSync(auditToolPath)) {
151
+ console.error(`${colors.red}Error: Audit tool not found at ${auditToolPath}${colors.reset}`);
152
+ return false;
153
+ }
154
+
155
+ const auditToolContent = fs.readFileSync(auditToolPath, 'utf8');
156
+ let valid = true;
157
+
158
+ // Check 1: Verify no hardcoded REQUIRED_PEERS array
159
+ const hardcodedPattern = /const\s+REQUIRED_PEERS\s*=\s*\[['"][^'"]+['"]/;
160
+ if (hardcodedPattern.test(auditToolContent)) {
161
+ console.error(`${colors.red}❌ Error: Found hardcoded REQUIRED_PEERS array in audit tool${colors.reset}`);
162
+ console.error(`${colors.yellow} → The audit tool should read required/optional peers from peerDependenciesMeta${colors.reset}`);
163
+ valid = false;
164
+ hasErrors = true;
165
+ }
166
+
167
+ // Check 2: Verify audit tool uses peerDependenciesMeta
168
+ if (!auditToolContent.includes('peerDependenciesMeta')) {
169
+ console.error(`${colors.red}❌ Error: Audit tool does not reference peerDependenciesMeta${colors.reset}`);
170
+ console.error(`${colors.yellow} → The audit tool should read from peerDependenciesMeta to determine required vs optional peers${colors.reset}`);
171
+ valid = false;
172
+ hasErrors = true;
173
+ }
174
+
175
+ // Check 3: Verify audit tool can read package.json
176
+ if (!auditToolContent.includes('findPaceCorePackageJson')) {
177
+ console.warn(`${colors.yellow}⚠️ Warning: Audit tool may not be able to find pace-core package.json${colors.reset}`);
178
+ hasWarnings = true;
179
+ }
180
+
181
+ // Check 4: Try to load and test the audit tool
182
+ try {
183
+ const { runDependencyAudit } = require(auditToolPath);
184
+ if (typeof runDependencyAudit !== 'function') {
185
+ console.error(`${colors.red}❌ Error: Audit tool does not export runDependencyAudit function${colors.reset}`);
186
+ valid = false;
187
+ hasErrors = true;
188
+ } else {
189
+ // Test that it can read package.json (use current directory as test)
190
+ const testResult = runDependencyAudit(path.resolve(__dirname, '../..'));
191
+ if (testResult.error && testResult.error.includes('package.json')) {
192
+ console.warn(`${colors.yellow}⚠️ Warning: Audit tool had issues reading package.json: ${testResult.error}${colors.reset}`);
193
+ hasWarnings = true;
194
+ }
195
+ }
196
+ } catch (error) {
197
+ console.error(`${colors.red}❌ Error: Could not load audit tool: ${error.message}${colors.reset}`);
198
+ valid = false;
199
+ hasErrors = true;
200
+ }
201
+
202
+ if (valid) {
203
+ console.log(`${colors.green}✅ Audit tool is consistent with package.json${colors.reset}`);
204
+ }
205
+
206
+ return valid;
207
+ }
208
+
209
+ /**
210
+ * Main validation function
211
+ */
212
+ function main() {
213
+ console.log(`${colors.bold}${colors.cyan}Dependency Validation${colors.reset}`);
214
+ console.log(`${colors.cyan}${'='.repeat(50)}${colors.reset}\n`);
215
+
216
+ const packageJsonValid = validatePackageJson();
217
+ console.log();
218
+ const auditToolValid = validateAuditTool();
219
+ console.log();
220
+
221
+ // Summary
222
+ console.log(`${colors.bold}Summary:${colors.reset}\n`);
223
+
224
+ if (hasErrors) {
225
+ console.log(`${colors.red}❌ Validation failed with errors${colors.reset}`);
226
+ console.log(`${colors.yellow}Fix the errors above before publishing${colors.reset}\n`);
227
+ process.exit(1);
228
+ } else if (hasWarnings) {
229
+ console.log(`${colors.yellow}⚠️ Validation passed with warnings${colors.reset}`);
230
+ console.log(`${colors.yellow}Review the warnings above${colors.reset}\n`);
231
+ process.exit(0);
232
+ } else {
233
+ console.log(`${colors.green}✅ All dependency validations passed!${colors.reset}\n`);
234
+ process.exit(0);
235
+ }
236
+ }
237
+
238
+ // Run if called directly
239
+ if (require.main === module) {
240
+ main();
241
+ }
242
+
243
+ // Export for use by other scripts
244
+ module.exports = {
245
+ validatePackageJson,
246
+ validateAuditTool,
247
+ main,
248
+ };
@@ -23,7 +23,7 @@ vi.mock('../test-utils', () => ({
23
23
  describe('[helpers] componentTestPatterns', () => {
24
24
  describe('testRenders', () => {
25
25
  it('creates test that renders component without crashing', () => {
26
- const TestComponent = () => <div data-testid="test-component">Test</div>;
26
+ const TestComponent = () => <p data-testid="test-component">Test</p>;
27
27
  const testFn = componentTestPatterns.testRenders(TestComponent);
28
28
 
29
29
  expect(typeof testFn).toBe('function');
@@ -33,7 +33,7 @@ describe('[helpers] componentTestPatterns', () => {
33
33
  });
34
34
 
35
35
  it('creates test that renders component with props', () => {
36
- const TestComponent = ({ name }: { name: string }) => <div data-testid="test-component">{name}</div>;
36
+ const TestComponent = ({ name }: { name: string }) => <p data-testid="test-component">{name}</p>;
37
37
  const testFn = componentTestPatterns.testRenders(TestComponent, { name: 'Test Name' });
38
38
 
39
39
  expect(typeof testFn).toBe('function');
@@ -14,11 +14,11 @@ import { TestProviderWrapper, renderWithProviders, createMockUseUnifiedAuth, use
14
14
  function TestComponent() {
15
15
  const auth = useUnifiedAuth();
16
16
  return (
17
- <div>
17
+ <p>
18
18
  <span data-testid="user-email">{auth.user?.email}</span>
19
19
  <span data-testid="is-authenticated">{auth.isAuthenticated ? 'true' : 'false'}</span>
20
20
  <span data-testid="app-name">{auth.appName}</span>
21
- </div>
21
+ </p>
22
22
  );
23
23
  }
24
24