@jmruthers/pace-core 0.5.139 → 0.5.141

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 (161) hide show
  1. package/README.md +2 -2
  2. package/dist/{DataTable-JXFCA2BJ.js → DataTable-EGIN2NKK.js} +3 -3
  3. package/dist/{EventLogo-rFL_kRjk.d.ts → EventLogo-B3V3otev.d.ts} +307 -1
  4. package/dist/{chunk-BOOI7GK2.js → chunk-3R472UXR.js} +117 -1
  5. package/dist/chunk-3R472UXR.js.map +1 -0
  6. package/dist/{chunk-5JMOHWDI.js → chunk-ALUN6O3G.js} +492 -324
  7. package/dist/chunk-ALUN6O3G.js.map +1 -0
  8. package/dist/{chunk-6DXZ6V5Q.js → chunk-PZV3XZKJ.js} +2 -2
  9. package/dist/{chunk-TLT2ZR3L.js → chunk-WKTQM2IC.js} +2 -2
  10. package/dist/components.d.ts +3 -1
  11. package/dist/components.js +15 -3
  12. package/dist/components.js.map +1 -1
  13. package/dist/index.d.ts +4 -2
  14. package/dist/index.js +18 -4
  15. package/dist/index.js.map +1 -1
  16. package/dist/rbac/index.d.ts +94 -1
  17. package/dist/rbac/index.js +4 -2
  18. package/dist/utils.d.ts +1 -1
  19. package/dist/utils.js +17 -5
  20. package/dist/utils.js.map +1 -1
  21. package/docs/api/README.md +2 -2
  22. package/docs/api/classes/ColumnFactory.md +1 -1
  23. package/docs/api/classes/ErrorBoundary.md +1 -1
  24. package/docs/api/classes/InvalidScopeError.md +1 -1
  25. package/docs/api/classes/MissingUserContextError.md +1 -1
  26. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  27. package/docs/api/classes/PermissionDeniedError.md +1 -1
  28. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  29. package/docs/api/classes/RBACAuditManager.md +1 -1
  30. package/docs/api/classes/RBACCache.md +1 -1
  31. package/docs/api/classes/RBACEngine.md +1 -1
  32. package/docs/api/classes/RBACError.md +1 -1
  33. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  34. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  35. package/docs/api/classes/StorageUtils.md +1 -1
  36. package/docs/api/enums/FileCategory.md +1 -1
  37. package/docs/api/interfaces/AggregateConfig.md +1 -1
  38. package/docs/api/interfaces/BadgeProps.md +1 -1
  39. package/docs/api/interfaces/ButtonProps.md +1 -1
  40. package/docs/api/interfaces/CalendarProps.md +40 -0
  41. package/docs/api/interfaces/CardProps.md +1 -1
  42. package/docs/api/interfaces/ColorPalette.md +1 -1
  43. package/docs/api/interfaces/ColorShade.md +1 -1
  44. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  45. package/docs/api/interfaces/DataRecord.md +1 -1
  46. package/docs/api/interfaces/DataTableAction.md +1 -1
  47. package/docs/api/interfaces/DataTableColumn.md +1 -1
  48. package/docs/api/interfaces/DataTableProps.md +1 -1
  49. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  50. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  51. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  52. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  53. package/docs/api/interfaces/EventLogoProps.md +1 -1
  54. package/docs/api/interfaces/ExportColumn.md +1 -1
  55. package/docs/api/interfaces/ExportOptions.md +1 -1
  56. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  57. package/docs/api/interfaces/FileMetadata.md +1 -1
  58. package/docs/api/interfaces/FileReference.md +1 -1
  59. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  60. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  61. package/docs/api/interfaces/FileUploadProps.md +1 -1
  62. package/docs/api/interfaces/FooterProps.md +1 -1
  63. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  64. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  65. package/docs/api/interfaces/InputProps.md +1 -1
  66. package/docs/api/interfaces/LabelProps.md +1 -1
  67. package/docs/api/interfaces/LoginFormProps.md +1 -1
  68. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  69. package/docs/api/interfaces/NavigationContextType.md +1 -1
  70. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  71. package/docs/api/interfaces/NavigationItem.md +1 -1
  72. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  73. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  74. package/docs/api/interfaces/Organisation.md +1 -1
  75. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  76. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  77. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  78. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  79. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  80. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  81. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  82. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  83. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  84. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  85. package/docs/api/interfaces/PaletteData.md +1 -1
  86. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  87. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  88. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  89. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  90. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  91. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  92. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  93. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  94. package/docs/api/interfaces/RBACConfig.md +1 -1
  95. package/docs/api/interfaces/RBACLogger.md +1 -1
  96. package/docs/api/interfaces/ResourcePermissions.md +155 -0
  97. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  98. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  99. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  100. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  101. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  102. package/docs/api/interfaces/RouteConfig.md +1 -1
  103. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  104. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  105. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  106. package/docs/api/interfaces/StorageConfig.md +1 -1
  107. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  108. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  109. package/docs/api/interfaces/StorageListOptions.md +1 -1
  110. package/docs/api/interfaces/StorageListResult.md +1 -1
  111. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  112. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  113. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  114. package/docs/api/interfaces/StyleImport.md +1 -1
  115. package/docs/api/interfaces/SwitchProps.md +1 -1
  116. package/docs/api/interfaces/TabsContentProps.md +9 -0
  117. package/docs/api/interfaces/TabsListProps.md +9 -0
  118. package/docs/api/interfaces/TabsProps.md +9 -0
  119. package/docs/api/interfaces/TabsTriggerProps.md +9 -0
  120. package/docs/api/interfaces/TextareaProps.md +53 -0
  121. package/docs/api/interfaces/ToastActionElement.md +1 -1
  122. package/docs/api/interfaces/ToastProps.md +1 -1
  123. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  124. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  125. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  126. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  127. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  128. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  129. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  130. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  131. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  132. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  133. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  134. package/docs/api/interfaces/UseResourcePermissionsOptions.md +34 -0
  135. package/docs/api/interfaces/UserEventAccess.md +1 -1
  136. package/docs/api/interfaces/UserMenuProps.md +1 -1
  137. package/docs/api/interfaces/UserProfile.md +1 -1
  138. package/docs/api/modules.md +289 -2
  139. package/docs/getting-started/examples/basic-auth-app.md +196 -0
  140. package/docs/getting-started/examples/full-featured-app.md +616 -0
  141. package/package.json +3 -1
  142. package/src/components/Calendar/Calendar.test.tsx +338 -0
  143. package/src/components/Calendar/Calendar.tsx +192 -0
  144. package/src/components/Calendar/index.ts +10 -0
  145. package/src/components/Tabs/Tabs.test.tsx +439 -0
  146. package/src/components/Tabs/Tabs.tsx +202 -0
  147. package/src/components/Tabs/index.ts +10 -0
  148. package/src/components/Textarea/Textarea.test.tsx +269 -0
  149. package/src/components/Textarea/Textarea.tsx +133 -0
  150. package/src/components/Textarea/index.ts +10 -0
  151. package/src/components/index.ts +11 -0
  152. package/src/index.ts +11 -0
  153. package/src/rbac/hooks/index.ts +2 -0
  154. package/src/rbac/hooks/useResourcePermissions.test.ts +633 -0
  155. package/src/rbac/hooks/useResourcePermissions.ts +235 -0
  156. package/src/utils/performance/bundleAnalysis.ts +17 -3
  157. package/dist/chunk-5JMOHWDI.js.map +0 -1
  158. package/dist/chunk-BOOI7GK2.js.map +0 -1
  159. /package/dist/{DataTable-JXFCA2BJ.js.map → DataTable-EGIN2NKK.js.map} +0 -0
  160. /package/dist/{chunk-6DXZ6V5Q.js.map → chunk-PZV3XZKJ.js.map} +0 -0
  161. /package/dist/{chunk-TLT2ZR3L.js.map → chunk-WKTQM2IC.js.map} +0 -0
@@ -0,0 +1,235 @@
1
+ /**
2
+ * @file useResourcePermissions Hook
3
+ * @package @jmruthers/pace-core
4
+ * @module RBAC/Hooks
5
+ * @since 1.0.0
6
+ *
7
+ * Hook to check permissions for a specific resource type.
8
+ * This hook centralizes the common pattern of checking create/update/delete/read
9
+ * permissions, eliminating ~30 lines of boilerplate code per hook usage.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * import { useResourcePermissions } from '@jmruthers/pace-core/rbac';
14
+ *
15
+ * function ContactsHook() {
16
+ * const { canCreate, canUpdate, canDelete } = useResourcePermissions('contacts');
17
+ *
18
+ * const addContact = async (data: ContactData) => {
19
+ * if (!canCreate('contacts')) {
20
+ * throw new Error("Permission denied: You do not have permission to create contacts.");
21
+ * }
22
+ * // ... perform mutation
23
+ * };
24
+ * }
25
+ * ```
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * // With read permissions enabled
30
+ * const { canRead } = useResourcePermissions('contacts', { enableRead: true });
31
+ *
32
+ * if (!canRead('contacts')) {
33
+ * return <PermissionDenied />;
34
+ * }
35
+ * ```
36
+ *
37
+ * @security
38
+ * - Requires organisation context (handled by useResolvedScope)
39
+ * - All permission checks are scoped to the current organisation/event/app context
40
+ * - Missing user context results in all permissions being denied
41
+ */
42
+
43
+ import { useMemo } from 'react';
44
+ import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
45
+ import { useOrganisations } from '../../hooks/useOrganisations';
46
+ import { useEvents } from '../../hooks/useEvents';
47
+ import { useResolvedScope } from './useResolvedScope';
48
+ import { useCan } from './usePermissions';
49
+ import type { Scope } from '../types';
50
+
51
+ export interface UseResourcePermissionsOptions {
52
+ /** Whether to check read permissions (default: false) */
53
+ enableRead?: boolean;
54
+ /** Whether scope resolution is required (default: true) */
55
+ requireScope?: boolean;
56
+ }
57
+
58
+ export interface ResourcePermissions {
59
+ /** Check if user can create resources of this type */
60
+ canCreate: (resource: string) => boolean;
61
+ /** Check if user can update resources of this type */
62
+ canUpdate: (resource: string) => boolean;
63
+ /** Check if user can delete resources of this type */
64
+ canDelete: (resource: string) => boolean;
65
+ /** Check if user can read resources of this type */
66
+ canRead: (resource: string) => boolean;
67
+ /** The resolved scope object (for advanced use cases) */
68
+ scope: Scope;
69
+ /** Whether any permission check is currently loading */
70
+ isLoading: boolean;
71
+ /** Error from any permission check or scope resolution */
72
+ error: Error | null;
73
+ }
74
+
75
+ /**
76
+ * Hook to check permissions for a specific resource
77
+ *
78
+ * This hook encapsulates the common pattern of checking create/update/delete/read
79
+ * permissions for a resource type. It handles scope resolution, user context,
80
+ * and provides a simple API for permission checking.
81
+ *
82
+ * @param resource - The resource name (e.g., 'contacts', 'risks', 'journal')
83
+ * @param options - Optional configuration
84
+ * @param options.enableRead - Whether to check read permissions (default: false)
85
+ * @param options.requireScope - Whether scope resolution is required (default: true)
86
+ * @returns Object with permission check functions and scope
87
+ *
88
+ * @example
89
+ * ```tsx
90
+ * function useContacts() {
91
+ * const { canCreate, canUpdate, canDelete } = useResourcePermissions('contacts');
92
+ *
93
+ * const addContact = async (data: ContactData) => {
94
+ * if (!canCreate('contacts')) {
95
+ * throw new Error("Permission denied");
96
+ * }
97
+ * // ... perform mutation
98
+ * };
99
+ * }
100
+ * ```
101
+ */
102
+ export function useResourcePermissions(
103
+ resource: string,
104
+ options: UseResourcePermissionsOptions = {}
105
+ ): ResourcePermissions {
106
+ const { enableRead = false, requireScope = true } = options;
107
+
108
+ // Get user and supabase client from UnifiedAuth
109
+ const { user, supabase } = useUnifiedAuth();
110
+
111
+ // Get selected organisation
112
+ const { selectedOrganisation } = useOrganisations();
113
+
114
+ // Get selected event (optional - wrap in try/catch)
115
+ let selectedEvent: { event_id: string } | null = null;
116
+ try {
117
+ const eventsContext = useEvents();
118
+ selectedEvent = eventsContext.selectedEvent;
119
+ } catch (error) {
120
+ // Event provider not available - continue without event context
121
+ // This is expected in some apps that don't use events
122
+ }
123
+
124
+ // Resolve scope for permission checks
125
+ const { resolvedScope, isLoading: scopeLoading, error: scopeError } = useResolvedScope({
126
+ supabase,
127
+ selectedOrganisationId: selectedOrganisation?.id || null,
128
+ selectedEventId: selectedEvent?.event_id || null
129
+ });
130
+
131
+ // Create fallback scope if resolvedScope is not available
132
+ const scope: Scope = resolvedScope || {
133
+ organisationId: selectedOrganisation?.id || '',
134
+ eventId: selectedEvent?.event_id || undefined,
135
+ appId: undefined
136
+ };
137
+
138
+ // Permission checks for create, update, delete
139
+ const { can: canCreateResult, isLoading: createLoading, error: createError } = useCan(
140
+ user?.id || '',
141
+ scope,
142
+ `create:${resource}` as const,
143
+ undefined, // pageId
144
+ true // useCache
145
+ );
146
+
147
+ const { can: canUpdateResult, isLoading: updateLoading, error: updateError } = useCan(
148
+ user?.id || '',
149
+ scope,
150
+ `update:${resource}` as const,
151
+ undefined, // pageId
152
+ true // useCache
153
+ );
154
+
155
+ const { can: canDeleteResult, isLoading: deleteLoading, error: deleteError } = useCan(
156
+ user?.id || '',
157
+ scope,
158
+ `delete:${resource}` as const,
159
+ undefined, // pageId
160
+ true // useCache
161
+ );
162
+
163
+ // Optional read permission check
164
+ const { can: canReadResult, isLoading: readLoading, error: readError } = useCan(
165
+ user?.id || '',
166
+ scope,
167
+ `read:${resource}` as const,
168
+ undefined, // pageId
169
+ true // useCache
170
+ );
171
+
172
+ // Aggregate loading states - any permission check or scope resolution loading
173
+ const isLoading = useMemo(() => {
174
+ return scopeLoading || createLoading || updateLoading || deleteLoading || (enableRead && readLoading);
175
+ }, [scopeLoading, createLoading, updateLoading, deleteLoading, readLoading, enableRead]);
176
+
177
+ // Aggregate errors - prefer scope error, then any permission error
178
+ const error = useMemo(() => {
179
+ if (scopeError) return scopeError;
180
+ if (createError) return createError;
181
+ if (updateError) return updateError;
182
+ if (deleteError) return deleteError;
183
+ if (enableRead && readError) return readError;
184
+ return null;
185
+ }, [scopeError, createError, updateError, deleteError, readError, enableRead]);
186
+
187
+ // Return wrapper functions that take resource name and return permission result
188
+ // Note: The resource parameter in the function is for consistency with the API,
189
+ // but we're checking permissions for the resource passed to the hook
190
+ return useMemo(() => ({
191
+ canCreate: (res: string) => {
192
+ // For now, we only check the resource passed to the hook
193
+ // Future enhancement could support checking different resources
194
+ if (res !== resource) {
195
+ return false;
196
+ }
197
+ return canCreateResult;
198
+ },
199
+ canUpdate: (res: string) => {
200
+ if (res !== resource) {
201
+ return false;
202
+ }
203
+ return canUpdateResult;
204
+ },
205
+ canDelete: (res: string) => {
206
+ if (res !== resource) {
207
+ return false;
208
+ }
209
+ return canDeleteResult;
210
+ },
211
+ canRead: (res: string) => {
212
+ if (!enableRead) {
213
+ return true; // If read checking is disabled, allow read
214
+ }
215
+ if (res !== resource) {
216
+ return false;
217
+ }
218
+ return canReadResult;
219
+ },
220
+ scope,
221
+ isLoading,
222
+ error
223
+ }), [
224
+ resource,
225
+ canCreateResult,
226
+ canUpdateResult,
227
+ canDeleteResult,
228
+ canReadResult,
229
+ enableRead,
230
+ scope,
231
+ isLoading,
232
+ error
233
+ ]);
234
+ }
235
+
@@ -14,7 +14,13 @@ interface ChunkInfo {
14
14
  }
15
15
 
16
16
  class BundleAnalyzer {
17
- private enabled = import.meta.env.MODE === 'development';
17
+ private get enabled(): boolean {
18
+ try {
19
+ return import.meta.env?.MODE === 'development';
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
18
24
 
19
25
  analyzeBundle(): BundleStats | null {
20
26
  if (!this.enabled) return null;
@@ -104,7 +110,11 @@ export const bundleAnalyzer = new BundleAnalyzer();
104
110
 
105
111
  // Helper to check if imports are optimized
106
112
  export function validateImportPattern(moduleId: string, importedItems: string[]): void {
107
- if (import.meta.env.MODE !== 'development') return;
113
+ try {
114
+ if (import.meta.env?.MODE !== 'development') return;
115
+ } catch {
116
+ return; // Silently skip in test environments
117
+ }
108
118
 
109
119
  const largeModules = ['lodash', 'moment', 'rxjs'];
110
120
  const isLargeModule = largeModules.some(mod => moduleId.includes(mod));
@@ -121,7 +131,11 @@ export function validateImportPattern(moduleId: string, importedItems: string[])
121
131
 
122
132
  // Monitor dynamic imports
123
133
  export function trackDynamicImport(moduleName: string): void {
124
- if (import.meta.env.MODE !== 'development') return;
134
+ try {
135
+ if (import.meta.env?.MODE !== 'development') return;
136
+ } catch {
137
+ return; // Silently skip in test environments
138
+ }
125
139
 
126
140
  // TODO: Replace with proper logging service integration
127
141
  // For now, we'll log to console for testing purposes