@jmruthers/pace-core 0.6.6 → 0.6.7
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.
- package/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +12 -13
- package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
- package/audit-tool/audits/02-project-structure.cjs +255 -0
- package/audit-tool/audits/03-architecture.cjs +196 -0
- package/audit-tool/audits/04-code-quality.cjs +149 -0
- package/audit-tool/audits/05-styling.cjs +224 -0
- package/audit-tool/audits/06-security-rbac.cjs +544 -0
- package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
- package/audit-tool/audits/08-testing-documentation.cjs +202 -0
- package/audit-tool/audits/09-operations.cjs +208 -0
- package/audit-tool/index.cjs +291 -0
- package/audit-tool/utils/code-utils.cjs +218 -0
- package/audit-tool/utils/file-utils.cjs +230 -0
- package/audit-tool/utils/report-utils.cjs +241 -0
- package/cursor-rules/00-standards-overview.mdc +156 -0
- package/cursor-rules/{00-pace-core-compliance.mdc → 01-pace-core-compliance.mdc} +187 -34
- package/cursor-rules/02-project-structure.mdc +37 -5
- package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +125 -11
- package/cursor-rules/04-code-quality.mdc +419 -0
- package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +55 -10
- package/cursor-rules/{09-rbac-compliance.mdc → 06-security-rbac.mdc} +62 -6
- package/cursor-rules/07-api-tech-stack.mdc +377 -0
- package/cursor-rules/08-testing-documentation.mdc +324 -0
- package/cursor-rules/09-operations.mdc +365 -0
- package/dist/DataTable-7PMH7XN7.js +15 -0
- package/dist/{DataTable-2N_tqbfq.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
- package/dist/{PublicPageProvider-BBH6Vqg7.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +26 -16
- package/dist/{chunk-FENMYN2U.js → chunk-5X4QLXRG.js} +1 -3
- package/dist/{chunk-4T7OBVTU.js → chunk-6F3IILHI.js} +1 -1
- package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
- package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
- package/dist/{chunk-7TYHROIV.js → chunk-BM4CQ5P3.js} +50 -8
- package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
- package/dist/{chunk-OHIK3MIO.js → chunk-GHYHJTYV.js} +2 -2
- package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
- package/dist/{chunk-LAZMKTTF.js → chunk-JGWDVX64.js} +281 -347
- package/dist/{chunk-MAGBIDNS.js → chunk-L4XMVJKY.js} +2 -2
- package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
- package/dist/{chunk-ZS5VO5JB.js → chunk-Q7Q7V5NV.js} +406 -451
- package/dist/{chunk-3O3WHILE.js → chunk-VBCS3DUA.js} +236 -60
- package/dist/{chunk-BVP2BCJF.js → chunk-ZKAWKYT4.js} +8 -8
- package/dist/components.d.ts +5 -4
- package/dist/components.js +27 -32
- package/dist/eslint-rules/index.cjs +22 -9
- package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
- package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
- package/dist/eslint-rules/rules/05-styling.cjs +61 -0
- package/dist/eslint-rules/rules/{rbac.cjs → 06-security-rbac.cjs} +26 -10
- package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
- package/dist/eslint-rules/rules/08-testing.cjs +94 -0
- package/dist/hooks.d.ts +5 -5
- package/dist/hooks.js +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +18 -17
- package/dist/rbac/index.js +6 -6
- package/dist/theming/runtime.d.ts +14 -1
- package/dist/theming/runtime.js +1 -1
- package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
- package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +8 -8
- package/docs/README.md +1 -1
- package/docs/api/modules.md +47 -31
- package/docs/api-reference/components.md +18 -20
- package/docs/api-reference/hooks.md +80 -80
- package/docs/api-reference/types.md +1 -1
- package/docs/api-reference/utilities.md +1 -1
- package/docs/architecture/README.md +1 -1
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +6 -6
- package/docs/core-concepts/permissions.md +6 -6
- package/docs/documentation-index.md +12 -18
- package/docs/getting-started/documentation-index.md +1 -1
- package/docs/getting-started/examples/README.md +4 -4
- package/docs/getting-started/examples/full-featured-app.md +1 -1
- package/docs/getting-started/faq.md +2 -2
- package/docs/getting-started/quick-reference.md +4 -4
- package/docs/implementation-guides/authentication.md +15 -15
- package/docs/implementation-guides/component-styling.md +1 -1
- package/docs/implementation-guides/data-tables.md +126 -33
- package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
- package/docs/implementation-guides/dynamic-colors.md +3 -3
- package/docs/implementation-guides/file-upload-storage.md +2 -2
- package/docs/implementation-guides/hierarchical-datatable.md +40 -60
- package/docs/implementation-guides/inactivity-tracking.md +3 -3
- package/docs/implementation-guides/large-datasets.md +3 -2
- package/docs/implementation-guides/organisation-security.md +2 -2
- package/docs/implementation-guides/performance.md +2 -2
- package/docs/implementation-guides/permission-enforcement.md +1 -1
- package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
- package/docs/migration/V0.4.0_rbac-migration.md +6 -6
- package/docs/rbac/README.md +5 -5
- package/docs/rbac/advanced-patterns.md +6 -6
- package/docs/rbac/api-reference.md +20 -20
- package/docs/rbac/event-based-apps.md +3 -3
- package/docs/rbac/examples.md +41 -41
- package/docs/rbac/getting-started.md +37 -37
- package/docs/rbac/performance.md +1 -1
- package/docs/rbac/quick-start.md +52 -52
- package/docs/rbac/secure-client-protection.md +1 -1
- package/docs/rbac/troubleshooting.md +1 -1
- package/docs/security/README.md +5 -5
- package/docs/standards/0-standards-overview.md +220 -0
- package/docs/standards/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +204 -185
- package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
- package/docs/standards/3-architecture-standards.md +606 -0
- package/docs/standards/4-code-quality-standards.md +728 -0
- package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
- package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
- package/docs/standards/7-api-tech-stack-standards.md +662 -0
- package/docs/standards/8-testing-documentation-standards.md +401 -0
- package/docs/standards/9-operations-standards.md +1102 -0
- package/docs/standards/README.md +203 -104
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/common-issues.md +2 -2
- package/docs/troubleshooting/debugging.md +9 -9
- package/docs/troubleshooting/migration.md +4 -4
- package/eslint-config-pace-core.cjs +21 -10
- package/package.json +6 -5
- package/scripts/install-cursor-rules.cjs +11 -243
- package/scripts/install-eslint-config.cjs +284 -0
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +10 -10
- package/src/__tests__/integration/UserProfile.test.tsx +14 -14
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
- package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
- package/src/__tests__/templates/component.test.template.tsx +18 -15
- package/src/components/Calendar/Calendar.tsx +201 -47
- package/src/components/ContextSelector/ContextSelector.tsx +137 -153
- package/src/components/DataTable/AUDIT_REPORT.md +293 -0
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
- package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
- package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
- package/src/components/DataTable/components/EditableRow.tsx +5 -7
- package/src/components/DataTable/components/EmptyState.tsx +10 -9
- package/src/components/DataTable/components/FilterRow.tsx +2 -4
- package/src/components/DataTable/components/ImportModal.tsx +124 -126
- package/src/components/DataTable/components/LoadingState.tsx +5 -6
- package/src/components/DataTable/components/SortIndicator.tsx +50 -0
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
- package/src/components/DataTable/components/index.ts +2 -1
- package/src/components/DataTable/types.ts +0 -18
- package/src/components/DataTable/utils/a11yUtils.ts +17 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
- package/src/components/DateTimeField/DateTimeField.tsx +7 -8
- package/src/components/Dialog/Dialog.test.tsx +1 -0
- package/src/components/Dialog/Dialog.tsx +25 -8
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
- package/src/components/FileUpload/FileUpload.test.tsx +52 -14
- package/src/components/FileUpload/FileUpload.tsx +112 -130
- package/src/components/Progress/Progress.tsx +2 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
- package/src/components/Select/Select.tsx +86 -77
- package/src/components/Select/types.ts +3 -0
- package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
- package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
- package/src/hooks/public/usePublicEvent.ts +5 -5
- package/src/hooks/public/usePublicEventLogo.ts +5 -5
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.ts +5 -5
- package/src/hooks/useAppConfig.ts +2 -2
- package/src/hooks/useEventTheme.test.ts +7 -7
- package/src/hooks/useEventTheme.ts +1 -4
- package/src/hooks/useFileDisplay.ts +2 -2
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
- package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
- package/src/providers/__tests__/EventProvider.test.tsx +61 -61
- package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
- package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
- package/src/styles/core.css +7 -0
- package/src/theming/__tests__/parseEventColours.test.ts +9 -3
- package/src/theming/parseEventColours.ts +22 -10
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
- package/src/utils/storage/README.md +1 -1
- package/cursor-rules/01-standards-compliance.mdc +0 -285
- package/cursor-rules/04-testing-standards.mdc +0 -270
- package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
- package/cursor-rules/06-code-quality.mdc +0 -311
- package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
- package/cursor-rules/10-error-handling-patterns.mdc +0 -179
- package/cursor-rules/11-performance-optimization.mdc +0 -169
- package/cursor-rules/12-ci-cd-integration.mdc +0 -150
- package/dist/DataTable-LRJL4IRV.js +0 -15
- package/dist/eslint-rules/rules/compliance.cjs +0 -348
- package/dist/eslint-rules/rules/components.cjs +0 -113
- package/dist/eslint-rules/rules/imports.cjs +0 -102
- package/docs/best-practices/README.md +0 -472
- package/docs/best-practices/accessibility.md +0 -604
- package/docs/best-practices/common-patterns.md +0 -516
- package/docs/best-practices/deployment.md +0 -1103
- package/docs/best-practices/performance.md +0 -1328
- package/docs/best-practices/security.md +0 -940
- package/docs/best-practices/testing.md +0 -1034
- package/docs/rbac/compliance/compliance-guide.md +0 -544
- package/docs/standards/01-standards-compliance.md +0 -188
- package/docs/standards/03-solid-principles.md +0 -39
- package/docs/standards/04-testing-standards.md +0 -36
- package/docs/standards/05-bug-reports-and-features.md +0 -27
- package/docs/standards/06-code-quality.md +0 -34
- package/docs/standards/07-tech-stack-compliance.md +0 -30
- package/docs/standards/10-error-handling-patterns.md +0 -401
- package/docs/standards/11-performance-optimization.md +0 -348
- package/docs/standards/12-ci-cd-integration.md +0 -370
- package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
- package/scripts/audit/audit-compliance.cjs +0 -1295
- package/scripts/audit/audit-components.cjs +0 -260
- package/scripts/audit/audit-rbac.cjs +0 -954
- package/scripts/audit/audit-standards.cjs +0 -1268
- package/scripts/audit/index.cjs +0 -1927
- package/src/components/DataTable/components/DataTableBody.tsx +0 -478
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
- package/src/components/DataTable/components/ExpandButton.tsx +0 -113
- package/src/components/DataTable/components/GroupHeader.tsx +0 -54
- package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
- package/src/components/DataTable/core/DataTableContext.tsx +0 -216
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
- package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
- package/src/components/DataTable/utils/debugTools.ts +0 -514
- package/src/eslint-rules/index.cjs +0 -22
- package/src/eslint-rules/rules/components.cjs +0 -113
- package/src/eslint-rules/rules/imports.cjs +0 -102
- package/src/eslint-rules/rules/rbac.cjs +0 -790
- package/src/eslint-rules/utils/helpers.cjs +0 -42
- package/src/eslint-rules/utils/manifest-loader.cjs +0 -75
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Enforce code quality standards including TypeScript, ESLint, formatting, performance, and accessibility
|
|
3
|
+
globs: ["src/**/*.{ts,tsx,js,jsx}"]
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
paceCoreVersion: "0.6.x"
|
|
6
|
+
rulesVersion: "2025-01-28"
|
|
7
|
+
---
|
|
8
|
+
# Code Quality Guide
|
|
9
|
+
|
|
10
|
+
**📚 Human-Readable Standard**: See [4-code-quality-standards.md](../../packages/core/docs/standards/4-code-quality-standards.md) for complete documentation.
|
|
11
|
+
|
|
12
|
+
This guide enforces code quality standards to ensure maintainable, performant, and accessible code.
|
|
13
|
+
|
|
14
|
+
## AI Agent Instructions
|
|
15
|
+
|
|
16
|
+
**When writing or modifying code, ALWAYS:**
|
|
17
|
+
1. **Use strict TypeScript** - Never use `any`, always use proper types or `unknown` with type guards
|
|
18
|
+
2. **Use discriminated unions** - Replace boolean flags with discriminated unions for better type safety
|
|
19
|
+
3. **Use ReadonlyArray** - Use `ReadonlyArray` for immutable arrays
|
|
20
|
+
4. **Ensure accessibility** - All components must be keyboard accessible, have ARIA labels, and visible focus states
|
|
21
|
+
5. **Use semantic HTML** - Use semantic elements (`<main>`, `<section>`, `<article>`, etc.) instead of `<div>` wrappers
|
|
22
|
+
6. **Memoize appropriately** - Use `useMemo` and `useCallback` for expensive computations and stable references
|
|
23
|
+
7. **Early returns** - Use early returns to reduce nesting and improve readability
|
|
24
|
+
|
|
25
|
+
**Decision Tree: Type Safety**
|
|
26
|
+
```
|
|
27
|
+
1. What type should I use?
|
|
28
|
+
├─ Known structure → Interface or type
|
|
29
|
+
├─ Truly unknown → unknown (with type guard)
|
|
30
|
+
└─ Multiple variants → Discriminated union (not boolean flags)
|
|
31
|
+
|
|
32
|
+
2. Is this an array that shouldn't be mutated?
|
|
33
|
+
├─ YES → ReadonlyArray<T>
|
|
34
|
+
└─ NO → Array<T> or T[]
|
|
35
|
+
|
|
36
|
+
3. Do I need multiple states?
|
|
37
|
+
├─ YES → Discriminated union (type: 'loading' | 'success' | 'error')
|
|
38
|
+
└─ NO → Single type
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## TypeScript Standards
|
|
42
|
+
|
|
43
|
+
### MUST: Use Strict Mode
|
|
44
|
+
|
|
45
|
+
**TypeScript MUST use strict mode:**
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
// tsconfig.json
|
|
49
|
+
{
|
|
50
|
+
"compilerOptions": {
|
|
51
|
+
"strict": true,
|
|
52
|
+
"noImplicitAny": true,
|
|
53
|
+
"strictNullChecks": true,
|
|
54
|
+
"strictFunctionTypes": true,
|
|
55
|
+
"strictBindCallApply": true,
|
|
56
|
+
"strictPropertyInitialization": true,
|
|
57
|
+
"noImplicitThis": true,
|
|
58
|
+
"alwaysStrict": true
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### MUST NOT: Use `any`
|
|
64
|
+
|
|
65
|
+
**MUST NOT use `any` type. Use `unknown` if type is truly unknown:**
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
// ❌ WRONG: Using any
|
|
69
|
+
function processData(data: any) { return data.value; }
|
|
70
|
+
|
|
71
|
+
// ✅ CORRECT: Using unknown with type guard or proper types
|
|
72
|
+
function processData(data: unknown) {
|
|
73
|
+
if (typeof data === 'object' && data !== null && 'value' in data) {
|
|
74
|
+
return (data as { value: string }).value;
|
|
75
|
+
}
|
|
76
|
+
throw new Error('Invalid data');
|
|
77
|
+
}
|
|
78
|
+
// Or: interface Data { value: string; } function processData(data: Data) { return data.value; }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### MUST: Use Discriminated Unions
|
|
82
|
+
|
|
83
|
+
**MUST use discriminated unions instead of boolean flags:**
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
// ❌ WRONG: Boolean flags (confusing: what if both are true?)
|
|
87
|
+
interface User { isAdmin: boolean; isGuest: boolean; }
|
|
88
|
+
|
|
89
|
+
// ✅ CORRECT: Discriminated union
|
|
90
|
+
type User = { type: 'admin'; permissions: Permission[] } | { type: 'guest'; limitedAccess: boolean } | { type: 'user'; role: UserRole };
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### SHOULD: Use ReadonlyArray
|
|
94
|
+
|
|
95
|
+
**SHOULD use ReadonlyArray for immutable arrays:**
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
// ✅ CORRECT - ReadonlyArray
|
|
99
|
+
function processItems(items: ReadonlyArray<Item>) {
|
|
100
|
+
// Can't mutate items
|
|
101
|
+
return items.map(item => transform(item));
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## ESLint Configuration
|
|
106
|
+
|
|
107
|
+
**Note**: ESLint configuration and enforcement is handled by ESLint (Layer 3). See [4-code-quality-standards.md](../../packages/core/docs/standards/4-code-quality-standards.md) for complete ESLint setup instructions.
|
|
108
|
+
|
|
109
|
+
**MUST:**
|
|
110
|
+
- Use pace-core ESLint config
|
|
111
|
+
- Fix all ESLint errors before committing
|
|
112
|
+
- Run `npm run lint` before committing
|
|
113
|
+
|
|
114
|
+
## Code Formatting
|
|
115
|
+
|
|
116
|
+
### MUST: Use Consistent Formatting
|
|
117
|
+
|
|
118
|
+
**MUST use Prettier or equivalent for consistent formatting:**
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
// .prettierrc
|
|
122
|
+
{
|
|
123
|
+
"semi": true,
|
|
124
|
+
"singleQuote": true,
|
|
125
|
+
"tabWidth": 2,
|
|
126
|
+
"trailingComma": "es5",
|
|
127
|
+
"printWidth": 100
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### MUST: Format Before Committing
|
|
132
|
+
|
|
133
|
+
**MUST format code before committing:**
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npm run format
|
|
137
|
+
# Or use pre-commit hook
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Performance Considerations
|
|
141
|
+
|
|
142
|
+
### SHOULD: Optimize Re-renders
|
|
143
|
+
|
|
144
|
+
**SHOULD use React.memo, useMemo, useCallback appropriately:**
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
// ✅ CORRECT: Memoize expensive computations, callbacks, and components
|
|
148
|
+
const expensiveValue = useMemo(() => computeExpensiveValue(data), [data]);
|
|
149
|
+
const handleClick = useCallback(() => doSomething(id), [id]);
|
|
150
|
+
const ExpensiveComponent = React.memo(({ data }) => <div>{/* ... */}</div>);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### SHOULD: Avoid Unnecessary Re-renders
|
|
154
|
+
|
|
155
|
+
**SHOULD avoid causing unnecessary re-renders:**
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
// ❌ WRONG: New object on every render (causes unnecessary re-renders)
|
|
159
|
+
function Component({ items }) { const config = { items, enabled: true }; return <Child config={config} />; }
|
|
160
|
+
|
|
161
|
+
// ✅ CORRECT: Memoize object
|
|
162
|
+
function Component({ items }) {
|
|
163
|
+
const config = useMemo(() => ({ items, enabled: true }), [items]);
|
|
164
|
+
return <Child config={config} />;
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### SHOULD: Lazy Load Heavy Components
|
|
169
|
+
|
|
170
|
+
**SHOULD lazy load heavy components:**
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
// ✅ CORRECT: Lazy load heavy components
|
|
174
|
+
import { lazy, Suspense } from 'react';
|
|
175
|
+
const HeavyComponent = lazy(() => import('./HeavyComponent'));
|
|
176
|
+
function App() {
|
|
177
|
+
return <Suspense fallback={<Loading />}><HeavyComponent /></Suspense>;
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Accessibility Requirements
|
|
182
|
+
|
|
183
|
+
**MUST** ensure all components and pages meet WCAG 2.1 Level AA standards.
|
|
184
|
+
|
|
185
|
+
### Core Principles
|
|
186
|
+
|
|
187
|
+
1. **Semantic HTML** - Use semantic elements (`<main>`, `<section>`, `<article>`, `<nav>`, etc.) instead of `<div>` wrappers
|
|
188
|
+
2. **Keyboard Navigation** - All interactive elements must be accessible via keyboard
|
|
189
|
+
3. **ARIA Labels** - Provide clear labels for screen readers when visible text isn't sufficient
|
|
190
|
+
4. **Focus Management** - Visible focus indicators on all interactive elements
|
|
191
|
+
5. **Color Contrast** - Text must meet 4.5:1 contrast ratio (WCAG AA)
|
|
192
|
+
|
|
193
|
+
### Implementation Requirements
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
// ✅ CORRECT - Semantic HTML with ARIA
|
|
197
|
+
<main>
|
|
198
|
+
<h1>Page Title</h1>
|
|
199
|
+
<section aria-labelledby="section-title">
|
|
200
|
+
<h2 id="section-title">Section Title</h2>
|
|
201
|
+
<Button
|
|
202
|
+
aria-label="Delete user"
|
|
203
|
+
aria-describedby="delete-help"
|
|
204
|
+
>
|
|
205
|
+
Delete
|
|
206
|
+
</Button>
|
|
207
|
+
<span id="delete-help" className="sr-only">
|
|
208
|
+
Permanently removes the user account
|
|
209
|
+
</span>
|
|
210
|
+
</section>
|
|
211
|
+
</main>
|
|
212
|
+
|
|
213
|
+
// ❌ WRONG - Non-semantic structure
|
|
214
|
+
<div>
|
|
215
|
+
<div className="title">Page Title</div>
|
|
216
|
+
<div>
|
|
217
|
+
<button>Delete</button>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Keyboard Navigation
|
|
223
|
+
|
|
224
|
+
**MUST** ensure all interactive elements are keyboard accessible:
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
// ✅ CORRECT - pace-core components handle keyboard navigation automatically
|
|
228
|
+
import { Button, DataTable } from '@jmruthers/pace-core';
|
|
229
|
+
|
|
230
|
+
<Button onClick={handleClick}>Click me</Button>
|
|
231
|
+
<DataTable data={data} columns={columns} />
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Screen Reader Support
|
|
235
|
+
|
|
236
|
+
**MUST** provide appropriate ARIA attributes:
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
// ✅ CORRECT - ARIA labels for icons
|
|
240
|
+
<Button aria-label="Close dialog">
|
|
241
|
+
<Icon name="x" aria-hidden="true" />
|
|
242
|
+
</Button>
|
|
243
|
+
|
|
244
|
+
// ✅ CORRECT - Error messages with role="alert"
|
|
245
|
+
{error && (
|
|
246
|
+
<div role="alert" aria-live="polite">
|
|
247
|
+
{error.message}
|
|
248
|
+
</div>
|
|
249
|
+
)}
|
|
250
|
+
|
|
251
|
+
// ✅ CORRECT - Loading states
|
|
252
|
+
<div role="status" aria-live="polite" aria-busy={loading}>
|
|
253
|
+
{loading && <span className="sr-only">Loading data...</span>}
|
|
254
|
+
{loading ? <Spinner /> : <Content />}
|
|
255
|
+
</div>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Testing Accessibility
|
|
259
|
+
|
|
260
|
+
**MUST** test with:
|
|
261
|
+
- Keyboard navigation (Tab, Enter, Space, Arrow keys)
|
|
262
|
+
- Screen readers (NVDA, JAWS, VoiceOver)
|
|
263
|
+
- Automated tools (axe DevTools, WAVE, Lighthouse)
|
|
264
|
+
|
|
265
|
+
**Accessibility Checklist:**
|
|
266
|
+
- [ ] All interactive elements keyboard accessible
|
|
267
|
+
- [ ] Visible focus indicators on all interactive elements
|
|
268
|
+
- [ ] ARIA labels provided for icons and images
|
|
269
|
+
- [ ] Semantic HTML used appropriately
|
|
270
|
+
- [ ] Error messages properly associated with form fields
|
|
271
|
+
- [ ] Color contrast meets WCAG AA (4.5:1 for text)
|
|
272
|
+
- [ ] Screen reader announcements work correctly
|
|
273
|
+
- [ ] Focus management in modals and dynamic content
|
|
274
|
+
|
|
275
|
+
## Code Review Checklist
|
|
276
|
+
|
|
277
|
+
**Before submitting code for review, verify:**
|
|
278
|
+
|
|
279
|
+
- [ ] TypeScript strict mode enabled
|
|
280
|
+
- [ ] No `any` types used
|
|
281
|
+
- [ ] ESLint passes with no errors
|
|
282
|
+
- [ ] Code is formatted consistently
|
|
283
|
+
- [ ] Performance optimizations applied where needed
|
|
284
|
+
- [ ] Accessibility requirements met
|
|
285
|
+
- [ ] Semantic HTML used
|
|
286
|
+
- [ ] ARIA labels provided
|
|
287
|
+
- [ ] Keyboard navigation works
|
|
288
|
+
- [ ] Focus indicators visible
|
|
289
|
+
- [ ] Code is readable and maintainable
|
|
290
|
+
- [ ] Comments explain "why", not "what"
|
|
291
|
+
|
|
292
|
+
## Code Complexity
|
|
293
|
+
|
|
294
|
+
### SHOULD: Keep Functions Small
|
|
295
|
+
|
|
296
|
+
**Functions SHOULD be small and focused:**
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
// ❌ WRONG: Large function (100+ lines, multiple responsibilities)
|
|
300
|
+
function processUserData(user) { /* 100+ lines of logic */ }
|
|
301
|
+
|
|
302
|
+
// ✅ CORRECT: Small, focused functions
|
|
303
|
+
function validateUser(user: User): ValidationResult { /* Validation logic */ }
|
|
304
|
+
function transformUser(user: User): TransformedUser { /* Transformation logic */ }
|
|
305
|
+
function processUserData(user: User) {
|
|
306
|
+
const validation = validateUser(user);
|
|
307
|
+
if (!validation.valid) return validation;
|
|
308
|
+
return transformUser(user);
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### SHOULD: Limit Cyclomatic Complexity
|
|
313
|
+
|
|
314
|
+
**Functions SHOULD have low cyclomatic complexity (< 10):**
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
// ❌ WRONG: High complexity (many nested conditions)
|
|
318
|
+
function processData(data) { if (condition1) { if (condition2) { if (condition3) { /* ... */ } } } }
|
|
319
|
+
|
|
320
|
+
// ✅ CORRECT: Lower complexity (early returns)
|
|
321
|
+
function processData(data) { if (!condition1) return; if (!condition2) return; if (!condition3) return; /* Process */ }
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Error Handling
|
|
325
|
+
|
|
326
|
+
### MUST: Handle Errors Gracefully
|
|
327
|
+
|
|
328
|
+
**MUST handle errors and provide user feedback:**
|
|
329
|
+
|
|
330
|
+
```tsx
|
|
331
|
+
// ✅ CORRECT: Handle errors gracefully with user feedback
|
|
332
|
+
try {
|
|
333
|
+
const data = await fetchData();
|
|
334
|
+
setData(data);
|
|
335
|
+
} catch (error) {
|
|
336
|
+
logger.error('Failed to fetch data', error);
|
|
337
|
+
toast.error('Failed to load data. Please try again.');
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### MUST: Use Type-Safe Error Handling
|
|
342
|
+
|
|
343
|
+
**MUST use type-safe error handling:**
|
|
344
|
+
|
|
345
|
+
```tsx
|
|
346
|
+
// ✅ CORRECT: Type-safe error handling
|
|
347
|
+
function isApiError(error: unknown): error is ApiError {
|
|
348
|
+
return typeof error === 'object' && error !== null && 'code' in error && 'message' in error;
|
|
349
|
+
}
|
|
350
|
+
try {
|
|
351
|
+
await apiCall();
|
|
352
|
+
} catch (error) {
|
|
353
|
+
if (isApiError(error)) handleApiError(error);
|
|
354
|
+
else handleUnknownError(error);
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Common Mistakes to Avoid
|
|
359
|
+
|
|
360
|
+
**When writing code, NEVER:**
|
|
361
|
+
1. **Use `any` type** - Always use proper types or `unknown` with type guards
|
|
362
|
+
```tsx
|
|
363
|
+
// ❌ WRONG
|
|
364
|
+
function process(data: any) {
|
|
365
|
+
return data.value;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ✅ CORRECT
|
|
369
|
+
function process(data: unknown) {
|
|
370
|
+
if (typeof data === 'object' && data !== null && 'value' in data) {
|
|
371
|
+
return (data as { value: string }).value;
|
|
372
|
+
}
|
|
373
|
+
throw new Error('Invalid data');
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
2. **Use boolean flags for multiple states** - Use discriminated unions
|
|
378
|
+
```tsx
|
|
379
|
+
// ❌ WRONG
|
|
380
|
+
interface User { isAdmin: boolean; isGuest: boolean; }
|
|
381
|
+
|
|
382
|
+
// ✅ CORRECT
|
|
383
|
+
type User =
|
|
384
|
+
| { type: 'admin'; permissions: Permission[] }
|
|
385
|
+
| { type: 'guest'; limitedAccess: boolean }
|
|
386
|
+
| { type: 'user'; role: UserRole };
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
3. **Forget accessibility** - Always ensure keyboard navigation, ARIA labels, and semantic HTML
|
|
390
|
+
4. **Create mutable arrays when they shouldn't change** - Use `ReadonlyArray`
|
|
391
|
+
5. **Skip memoization for expensive operations** - Use `useMemo` and `useCallback` appropriately
|
|
392
|
+
|
|
393
|
+
## Code Quality Checklist
|
|
394
|
+
|
|
395
|
+
Before committing, verify:
|
|
396
|
+
- [ ] TypeScript strict mode enabled
|
|
397
|
+
- [ ] No `any` types (use `unknown` with type guards)
|
|
398
|
+
- [ ] Discriminated unions used instead of boolean flags
|
|
399
|
+
- [ ] `ReadonlyArray` used for immutable arrays
|
|
400
|
+
- [ ] Naming conventions followed (hooks: `use*`, providers: `*Provider`, etc.)
|
|
401
|
+
- [ ] Pure functions preferred (no side effects)
|
|
402
|
+
- [ ] Early returns used to reduce nesting
|
|
403
|
+
- [ ] Complex logic extracted to helpers
|
|
404
|
+
- [ ] Components are small and focused
|
|
405
|
+
- [ ] Functional components only (no class components)
|
|
406
|
+
- [ ] Hook dependencies are complete
|
|
407
|
+
- [ ] Memoization used when appropriate
|
|
408
|
+
- [ ] Accessibility requirements met (WCAG 2.1 AA)
|
|
409
|
+
- [ ] Keyboard navigation supported
|
|
410
|
+
- [ ] ARIA labels provided where needed
|
|
411
|
+
- [ ] Semantic HTML used appropriately
|
|
412
|
+
|
|
413
|
+
## Reference
|
|
414
|
+
|
|
415
|
+
**Note**: ESLint configuration details and mechanical type checking are handled by ESLint (Layer 3). See [4-code-quality-standards.md](../../packages/core/docs/standards/4-code-quality-standards.md) for complete enforcement details.
|
|
416
|
+
|
|
417
|
+
- TypeScript Handbook: https://www.typescriptlang.org/docs/
|
|
418
|
+
- React Performance: https://react.dev/learn/render-and-commit
|
|
419
|
+
- Web Accessibility: https://www.w3.org/WAI/
|
|
@@ -7,15 +7,49 @@ rulesVersion: "2025-01-28"
|
|
|
7
7
|
---
|
|
8
8
|
# Markup Quality Guide
|
|
9
9
|
|
|
10
|
-
**📚 Human-Readable Standard**: See [
|
|
10
|
+
**📚 Human-Readable Standard**: See [5-styling-standards.md](../../packages/core/docs/standards/5-styling-standards.md) for complete documentation including **CRITICAL CSS configuration requirements**.
|
|
11
11
|
|
|
12
12
|
**⚠️ IMPORTANT**: This rule is ALWAYS APPLIED. The standard includes required CSS setup that MUST be followed for pace-core components to render correctly.
|
|
13
13
|
|
|
14
|
+
## AI Agent Instructions
|
|
15
|
+
|
|
16
|
+
**When writing or modifying code, ALWAYS:**
|
|
17
|
+
1. **Use pace-core components** - Never use native HTML elements when pace-core provides components
|
|
18
|
+
2. **Use semantic HTML** - Always use semantic elements (`<main>`, `<section>`, `<article>`, etc.) instead of `<div>` wrappers
|
|
19
|
+
3. **Never use inline styles** - Never use `style={{...}}`, always use pace-core components or Tailwind classes
|
|
20
|
+
4. **Use Grid for layout** - Prefer CSS Grid over Flexbox for centering, two-dimensional layouts, and full-page layouts
|
|
21
|
+
5. **Minimize elements** - Use the fewest elements possible, prefer React Fragments over wrapper divs
|
|
22
|
+
6. **Check CSS setup** - Before using pace-core components, verify CSS is configured correctly (see checklist below)
|
|
23
|
+
|
|
24
|
+
**Decision Tree: Markup & Styling**
|
|
25
|
+
```
|
|
26
|
+
1. What element should I use?
|
|
27
|
+
├─ Button → <Button> from pace-core (not <button>)
|
|
28
|
+
├─ Input → <Input> from pace-core (not <input>)
|
|
29
|
+
├─ Page wrapper → <main> (not <div>)
|
|
30
|
+
├─ Section → <section> (not <div>)
|
|
31
|
+
├─ Article → <article> (not <div>)
|
|
32
|
+
└─ Navigation → <nav> (not <div>)
|
|
33
|
+
|
|
34
|
+
2. Do I need a wrapper?
|
|
35
|
+
├─ For React single root → Use Fragment (<>...</>)
|
|
36
|
+
├─ For styling only → Apply to existing semantic parent
|
|
37
|
+
└─ For layout → Use Grid (grid place-items-center, grid grid-cols-*)
|
|
38
|
+
|
|
39
|
+
3. How do I center content?
|
|
40
|
+
├─ Both axes → grid place-items-center
|
|
41
|
+
└─ Single axis → flex (but prefer grid)
|
|
42
|
+
|
|
43
|
+
4. Am I using inline styles?
|
|
44
|
+
├─ YES → WRONG - Use pace-core component or Tailwind class
|
|
45
|
+
└─ NO → Continue
|
|
46
|
+
```
|
|
47
|
+
|
|
14
48
|
## ⚠️ CRITICAL: CSS Configuration Required
|
|
15
49
|
|
|
16
50
|
**Before using pace-core components, you MUST configure CSS correctly.** Without proper configuration, pace-core components will appear unstyled or with incorrect styling.
|
|
17
51
|
|
|
18
|
-
**MUST read the standard for complete CSS setup instructions**: [
|
|
52
|
+
**MUST read the standard for complete CSS setup instructions**: [5-styling-standards.md](../../packages/core/docs/standards/5-styling-standards.md)
|
|
19
53
|
|
|
20
54
|
**Quick checklist:**
|
|
21
55
|
- [ ] `src/app.css` exists with `@import "tailwindcss";`
|
|
@@ -38,18 +72,29 @@ This guide enforces clean markup standards, semantic HTML usage, and proper pace
|
|
|
38
72
|
|
|
39
73
|
```tsx
|
|
40
74
|
// ❌ WRONG: Custom button or native HTML element
|
|
41
|
-
<button className="btn-primary"
|
|
42
|
-
|
|
75
|
+
<button className="btn-primary" onClick={handleClick}>
|
|
76
|
+
Click me
|
|
77
|
+
</button>
|
|
78
|
+
<input
|
|
79
|
+
type="text"
|
|
80
|
+
className="form-input"
|
|
81
|
+
value={value}
|
|
82
|
+
onChange={handleChange}
|
|
83
|
+
/>
|
|
43
84
|
|
|
44
85
|
// ✅ CORRECT: Use pace-core components
|
|
45
86
|
import { Button, Input } from '@jmruthers/pace-core';
|
|
46
|
-
<Button>Click me</Button>
|
|
47
|
-
<Input
|
|
87
|
+
<Button onClick={handleClick}>Click me</Button>
|
|
88
|
+
<Input
|
|
89
|
+
type="text"
|
|
90
|
+
value={value}
|
|
91
|
+
onChange={handleChange}
|
|
92
|
+
/>
|
|
48
93
|
```
|
|
49
94
|
|
|
50
95
|
### MUST NOT: Add Custom Styles to pace-core Components
|
|
51
96
|
|
|
52
|
-
**
|
|
97
|
+
**NEVER add custom styles when pace-core components already provide styling. Use component variants/props instead.**
|
|
53
98
|
|
|
54
99
|
```tsx
|
|
55
100
|
// ❌ WRONG: Overriding pace-core component styles
|
|
@@ -111,7 +156,7 @@ import { Button, Input } from '@jmruthers/pace-core';
|
|
|
111
156
|
|
|
112
157
|
### MUST: Prefer Semantic HTML (Limit `<div>`)
|
|
113
158
|
|
|
114
|
-
**
|
|
159
|
+
**ALWAYS prefer semantic HTML elements** (`<main>`, `<section>`, `<article>`, `<header>`, `<footer>`, `<nav>`, lists, etc.).
|
|
115
160
|
Using semantic elements improves accessibility, maintainability, and consistency.
|
|
116
161
|
|
|
117
162
|
#### `<div>` usage policy
|
|
@@ -470,8 +515,8 @@ Before committing code, verify:
|
|
|
470
515
|
|
|
471
516
|
## Reference
|
|
472
517
|
|
|
473
|
-
- **pace-core Components**: See
|
|
518
|
+
- **pace-core Components**: See [01-pace-core-compliance.mdc](./01-pace-core-compliance.mdc) for component usage
|
|
474
519
|
- **Semantic HTML**: https://developer.mozilla.org/en-US/docs/Glossary/Semantics
|
|
475
520
|
- **React Fragments**: https://react.dev/reference/react/Fragment
|
|
476
|
-
- **Accessibility**: See
|
|
521
|
+
- **Accessibility**: See [04-code-quality.mdc](./04-code-quality.mdc) for accessibility requirements
|
|
477
522
|
- **When in doubt**: Remove the element, remove the style, and rely on pace-core or semantic HTML
|
|
@@ -1,18 +1,48 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Enforce RBAC contract compliance
|
|
3
|
-
globs: ["src/**/*.{ts,tsx,js,jsx}"]
|
|
2
|
+
description: Enforce RBAC contract compliance, RLS policy patterns, and security requirements
|
|
3
|
+
globs: ["src/**/*.{ts,tsx,js,jsx}", "supabase/migrations/**/*.sql"]
|
|
4
4
|
alwaysApply: false
|
|
5
5
|
paceCoreVersion: "0.6.x"
|
|
6
6
|
rulesVersion: "2025-01-28"
|
|
7
7
|
---
|
|
8
|
-
# RBAC
|
|
8
|
+
# Security & RBAC Standards Guide
|
|
9
9
|
|
|
10
|
-
**📚 Human-Readable Standard**: See [
|
|
10
|
+
**📚 Human-Readable Standard**: See [6-security-rbac-standards.md](../../packages/core/docs/standards/6-security-rbac-standards.md) for complete documentation including RLS policy patterns, helper functions, and security requirements.
|
|
11
11
|
|
|
12
|
-
This guide enforces the **mandatory RBAC contract** between pace-core and consuming apps.
|
|
12
|
+
This guide enforces the **mandatory RBAC contract** and security patterns between pace-core and consuming apps.
|
|
13
13
|
|
|
14
14
|
**⚠️ CRITICAL**: This contract is **non-negotiable**. See [RBAC Contract](../../packages/core/docs/rbac/RBAC_CONTRACT.md) for complete documentation.
|
|
15
15
|
|
|
16
|
+
## AI Agent Instructions
|
|
17
|
+
|
|
18
|
+
**When writing or modifying code, ALWAYS:**
|
|
19
|
+
1. **Protect all pages** - Wrap every protected page with `PagePermissionGuard` (no exceptions)
|
|
20
|
+
2. **Use pace-core hooks directly** - Never create wrapper functions around `useCan` or `useResourcePermissions`
|
|
21
|
+
3. **Use RESOURCE_NAMES constants** - Never use string literals in `useResourcePermissions` calls
|
|
22
|
+
4. **Validate all inputs** - Always validate user input with Zod schemas before processing
|
|
23
|
+
5. **Never expose internals** - Never show SQL errors, stack traces, or internal details to users
|
|
24
|
+
6. **Use helper functions in RLS** - Always use STABLE SECURITY DEFINER helper functions, never subqueries
|
|
25
|
+
7. **Sanitize logs** - Never log passwords, tokens, or sensitive data
|
|
26
|
+
|
|
27
|
+
**Decision Tree: Security & Permissions**
|
|
28
|
+
```
|
|
29
|
+
1. Is this a protected page?
|
|
30
|
+
├─ YES → Wrap with PagePermissionGuard (no exceptions)
|
|
31
|
+
└─ NO → Continue
|
|
32
|
+
|
|
33
|
+
2. Do I need to check permissions?
|
|
34
|
+
├─ YES → Use pace-core hooks directly (useCan, useResourcePermissions)
|
|
35
|
+
└─ NO → Continue
|
|
36
|
+
|
|
37
|
+
3. Am I handling user input?
|
|
38
|
+
├─ YES → Validate with Zod schema
|
|
39
|
+
└─ NO → Continue
|
|
40
|
+
|
|
41
|
+
4. Am I logging or showing errors?
|
|
42
|
+
├─ YES → Never expose internals, sanitize sensitive data
|
|
43
|
+
└─ NO → Continue
|
|
44
|
+
```
|
|
45
|
+
|
|
16
46
|
## MUST: Use PagePermissionGuard for All Protected Pages
|
|
17
47
|
|
|
18
48
|
**MUST wrap all protected pages with `PagePermissionGuard`:**
|
|
@@ -358,6 +388,32 @@ These rules are enforced by ESLint rules (all ERROR severity):
|
|
|
358
388
|
7. **`no-resource-permission-string-literals`** - Detects string literals in `useResourcePermissions` calls
|
|
359
389
|
8. **`no-permission-wrapper-functions`** - Detects wrapper functions around pace-core permission hooks
|
|
360
390
|
|
|
391
|
+
## Common Mistakes to Avoid
|
|
392
|
+
|
|
393
|
+
**When writing code, NEVER:**
|
|
394
|
+
1. **Skip PagePermissionGuard** - Every protected page must be wrapped
|
|
395
|
+
```tsx
|
|
396
|
+
// ❌ WRONG
|
|
397
|
+
function DashboardPage() {
|
|
398
|
+
return <DashboardContent />;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// ✅ CORRECT
|
|
402
|
+
function DashboardPage() {
|
|
403
|
+
return (
|
|
404
|
+
<PagePermissionGuard pageName="dashboard" operation="read">
|
|
405
|
+
<DashboardContent />
|
|
406
|
+
</PagePermissionGuard>
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
2. **Create wrapper functions around permission hooks** - Use hooks directly
|
|
412
|
+
3. **Use string literals in useResourcePermissions** - Always use RESOURCE_NAMES constants
|
|
413
|
+
4. **Bypass input validation** - Always validate with Zod schemas
|
|
414
|
+
5. **Expose internal errors** - Never show SQL, stack traces, or file paths to users
|
|
415
|
+
6. **Log sensitive data** - Never log passwords, tokens, or PII
|
|
416
|
+
|
|
361
417
|
## Compliance Checklist
|
|
362
418
|
|
|
363
419
|
Before committing RBAC-related code, verify:
|
|
@@ -459,4 +515,4 @@ $$;
|
|
|
459
515
|
- **Migration Guide**: `packages/core/docs/rbac/MIGRATION_GUIDE.md` - Migrating to RBAC Contract v2.0.0
|
|
460
516
|
- **Quick Start**: `packages/core/docs/rbac/quick-start.md` - Getting started guide
|
|
461
517
|
- **API Reference**: `packages/core/docs/rbac/api-reference.md` - Complete API documentation
|
|
462
|
-
- **RLS Standards**:
|
|
518
|
+
- **RLS Standards**: [6-security-rbac-standards.md](../../packages/core/docs/standards/6-security-rbac-standards.md) - RLS policy patterns and super-admin requirements
|