@memberjunction/react-runtime 4.0.0 → 4.1.0

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/README.md CHANGED
@@ -1,21 +1,70 @@
1
1
  # @memberjunction/react-runtime
2
2
 
3
- Platform-agnostic React component runtime for MemberJunction. This package provides core compilation, registry, execution capabilities, and dynamic library management for React components in any JavaScript environment.
3
+ Platform-agnostic React component runtime for MemberJunction. Provides core compilation, registry, dynamic library management, and execution capabilities for React components in any JavaScript environment.
4
+
5
+ ## Architecture
6
+
7
+ ```mermaid
8
+ graph TD
9
+ subgraph "@memberjunction/react-runtime"
10
+ A[createReactRuntime] --> B[ComponentCompiler]
11
+ A --> C[ComponentRegistry]
12
+ A --> D[ComponentResolver]
13
+ A --> E[ComponentManager]
14
+
15
+ B --> F["Babel Standalone<br/>(JSX Compilation)"]
16
+
17
+ C --> G[Component Store]
18
+ C --> H[Namespace Support]
19
+
20
+ D --> I[Component Resolution]
21
+ D --> J[Dependency Resolution]
22
+
23
+ E --> K[Load + Compile + Register]
24
+
25
+ subgraph "Utilities"
26
+ L[LibraryLoader]
27
+ M[LibraryRegistry]
28
+ N[CacheManager]
29
+ O[ResourceManager]
30
+ P[ErrorBoundary]
31
+ Q[PropBuilder]
32
+ R[ReactRootManager]
33
+ end
34
+ end
35
+
36
+ subgraph "Runtime APIs"
37
+ S[ComponentHierarchyRegistrar]
38
+ T[StandardLibraries]
39
+ U[ComponentErrorAnalyzer]
40
+ end
41
+
42
+ style A fill:#2d6a9f,stroke:#1a4971,color:#fff
43
+ style B fill:#2d8659,stroke:#1a5c3a,color:#fff
44
+ style C fill:#2d8659,stroke:#1a5c3a,color:#fff
45
+ style D fill:#7c5295,stroke:#563a6b,color:#fff
46
+ style E fill:#7c5295,stroke:#563a6b,color:#fff
47
+ style L fill:#b8762f,stroke:#8a5722,color:#fff
48
+ style N fill:#b8762f,stroke:#8a5722,color:#fff
49
+ style P fill:#b8762f,stroke:#8a5722,color:#fff
50
+ ```
4
51
 
5
52
  ## Overview
6
53
 
7
- The React Runtime package enables dynamic compilation and execution of React components from source code, with flexible external library management. It works in both browser and Node.js environments, making it suitable for client-side rendering, server-side testing, and multi-tenant applications with different library requirements.
54
+ This package enables dynamic compilation and execution of React components at runtime. Components can be loaded from MemberJunction's database, compiled with Babel, registered into a component registry, and rendered with full dependency resolution.
8
55
 
9
- ## Features
56
+ **Key capabilities:**
10
57
 
11
- - **Dynamic Compilation**: Transform JSX and React code at runtime using Babel
12
- - **Component Registry**: Manage compiled components with namespace support
13
- - **Dependency Resolution**: Handle component hierarchies and dependencies
14
- - **Dynamic Library Management**: Configure and load external libraries at runtime
15
- - **Error Boundaries**: Comprehensive error handling for React components
16
- - **Platform Agnostic**: Works in any JavaScript environment
17
- - **Type Safe**: Full TypeScript support with strict typing
18
- - **Organization-Specific Libraries**: Support for different library sets per organization
58
+ - **Component Compilation**: Transpiles JSX/TSX source code using Babel standalone
59
+ - **Component Registry**: LRU-cached registry with namespace support for up to 1000 components
60
+ - **Component Resolution**: Resolves component specs against the registry with dependency tracking
61
+ - **Component Manager**: Unified API for loading, compiling, and registering components in one call
62
+ - **Library Management**: Dynamic library loading, registration, and dependency resolution
63
+ - **Error Boundaries**: Configurable error boundary creation with logging
64
+ - **Prop Building**: Type-safe prop construction with callback normalization and style processing
65
+ - **React Root Management**: Managed React root lifecycle for mounting/unmounting components
66
+ - **Caching**: LRU cache with configurable TTL for compiled components
67
+ - **UMD Build**: Ships both CommonJS and UMD bundles for browser environments
19
68
 
20
69
  ## Installation
21
70
 
@@ -23,737 +72,139 @@ The React Runtime package enables dynamic compilation and execution of React com
23
72
  npm install @memberjunction/react-runtime
24
73
  ```
25
74
 
26
- ## Basic Usage
75
+ ## Usage
27
76
 
28
- ### Creating a Runtime Instance
77
+ ### Quick Start
29
78
 
30
79
  ```typescript
31
80
  import { createReactRuntime } from '@memberjunction/react-runtime';
32
81
  import * as Babel from '@babel/standalone';
33
82
 
34
- // Create runtime with Babel instance
35
- const runtime = createReactRuntime(Babel);
36
-
37
- // The runtime now includes the unified ComponentManager
38
- const { compiler, registry, resolver, manager } = runtime;
39
- ```
40
-
41
- ## NEW: Unified ComponentManager (Recommended)
42
-
43
- The ComponentManager is a new unified API that simplifies component loading by handling fetching, compilation, registration, and caching in a single, efficient operation. It eliminates duplicate work and provides better performance.
44
-
45
- ### Why Use ComponentManager?
46
-
47
- - **Single API**: One method handles everything - no need to coordinate multiple components
48
- - **Efficient**: Automatically prevents duplicate fetching and compilation
49
- - **Smart Caching**: Multi-level caching with automatic invalidation
50
- - **Registry Tracking**: Built-in usage tracking for licensing compliance
51
- - **Better Error Handling**: Comprehensive error reporting with phases
52
-
53
- ### Loading a Component Hierarchy
54
-
55
- ```typescript
56
- import { ComponentSpec } from '@memberjunction/interactive-component-types';
57
-
58
- const componentSpec: ComponentSpec = {
59
- name: 'Dashboard',
60
- location: 'registry',
61
- registry: 'SkipAI',
62
- namespace: 'analytics',
63
- version: '1.0.0',
64
- dependencies: [
65
- { name: 'Chart', location: 'registry', registry: 'SkipAI' },
66
- { name: 'Grid', location: 'embedded', code: '...' }
67
- ]
68
- };
69
-
70
- // Load the entire hierarchy with one call
71
- const result = await runtime.manager.loadHierarchy(componentSpec, {
72
- contextUser: currentUser,
73
- defaultNamespace: 'Global',
74
- defaultVersion: 'latest',
75
- returnType: 'both'
76
- });
77
-
78
- if (result.success) {
79
- // Everything is loaded and ready
80
- const rootComponent = result.rootComponent;
81
- const resolvedSpec = result.resolvedSpec;
82
-
83
- console.log(`Loaded ${result.loadedComponents.length} components`);
84
- console.log(`Stats:`, result.stats);
85
- }
86
- ```
87
-
88
- ### Loading a Single Component
89
-
90
- ```typescript
91
- // For simple single component loading
92
- const result = await runtime.manager.loadComponent(componentSpec, {
93
- contextUser: currentUser,
94
- forceRefresh: false, // Use cache if available
95
- trackUsage: true // Track usage for licensing
96
- });
97
-
98
- if (result.success) {
99
- const component = result.component;
100
- const wasFromCache = result.fromCache;
101
- }
102
- ```
103
-
104
- ### Configuration Options
105
-
106
- ```typescript
107
83
  const runtime = createReactRuntime(Babel, {
108
- manager: {
109
- debug: true, // Enable debug logging
110
- maxCacheSize: 100, // Max cached specs
111
- cacheTTL: 3600000, // 1 hour cache TTL
112
- enableUsageTracking: true, // Track registry usage
113
- dependencyBatchSize: 5, // Parallel dependency loading
114
- fetchTimeout: 30000 // 30 second timeout
115
- }
84
+ compiler: { minify: false, sourceMaps: true, cache: true },
85
+ registry: { maxComponents: 500 }
116
86
  });
117
- ```
118
87
 
119
- ### Cache Management
88
+ // Compile a component from source
89
+ const compiled = runtime.compiler.compile(`
90
+ function MyComponent({ name }) {
91
+ return <div>Hello, {name}!</div>;
92
+ }
93
+ `);
120
94
 
121
- ```typescript
122
- // Clear all caches
123
- runtime.manager.clearCache();
95
+ // Register it
96
+ runtime.registry.register('MyComponent', compiled);
124
97
 
125
- // Get cache statistics
126
- const stats = runtime.manager.getCacheStats();
127
- console.log(`Cached specs: ${stats.fetchCacheSize}`);
128
- console.log(`Usage notifications: ${stats.notificationsCount}`);
98
+ // Resolve for rendering
99
+ const resolved = runtime.resolver.resolve({ name: 'MyComponent' });
129
100
  ```
130
101
 
131
- ## Legacy Approach (Still Supported)
132
-
133
- ### Compiling a Component (Old Way)
102
+ ### Component Manager (Unified API)
134
103
 
135
104
  ```typescript
136
- const componentCode = `
137
- function MyComponent({ data, userState, callbacks }) {
138
- return (
139
- <div>
140
- <h1>Hello, {data.name}!</h1>
141
- <button onClick={() => callbacks.RefreshData()}>
142
- Refresh
143
- </button>
144
- </div>
145
- );
146
- }
147
- `;
148
-
149
- // Compile the component
150
- const result = await runtime.compiler.compile({
151
- componentName: 'MyComponent',
152
- componentCode: componentCode
105
+ const result = await runtime.manager.load({
106
+ componentSpec: { name: 'MyWidget', source: jsxSource },
107
+ autoRegister: true,
108
+ resolveLibraries: true
153
109
  });
154
110
 
155
111
  if (result.success) {
156
- // Register the compiled component
157
- runtime.registry.register(
158
- 'MyComponent',
159
- result.component.component,
160
- 'MyNamespace',
161
- 'v1'
162
- );
112
+ // Component is compiled, registered, and ready to render
113
+ const Component = result.component;
163
114
  }
164
115
  ```
165
116
 
166
- ## Dynamic Library Management (New)
167
-
168
- ### Loading Libraries with Configuration
117
+ ### Library Management
169
118
 
170
119
  ```typescript
171
- import { LibraryLoader, StandardLibraryManager } from '@memberjunction/react-runtime';
172
-
173
- // Define custom library configuration
174
- const libraryConfig = {
175
- libraries: [
176
- {
177
- id: 'lodash',
178
- name: 'lodash',
179
- displayName: 'Lodash',
180
- category: 'utility',
181
- globalVariable: '_',
182
- version: '4.17.21',
183
- cdnUrl: 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js',
184
- description: 'Utility library',
185
- isEnabled: true,
186
- isCore: false
187
- },
188
- {
189
- id: 'chart-js',
190
- name: 'Chart',
191
- displayName: 'Chart.js',
192
- category: 'charting',
193
- globalVariable: 'Chart',
194
- version: '4.4.0',
195
- cdnUrl: 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.js',
196
- isEnabled: true,
197
- isCore: false
198
- }
199
- // ... more libraries
200
- ],
201
- metadata: {
202
- version: '1.0.0',
203
- lastUpdated: '2024-01-01'
204
- }
205
- };
206
-
207
- // Load libraries with custom configuration
208
- const result = await LibraryLoader.loadAllLibraries(libraryConfig);
209
- ```
210
-
211
- ### Managing Library Configurations
212
-
213
- ```typescript
214
- import { StandardLibraryManager } from '@memberjunction/react-runtime';
120
+ import { LibraryRegistry, LibraryLoader } from '@memberjunction/react-runtime';
215
121
 
216
- // Set a custom configuration
217
- StandardLibraryManager.setConfiguration(libraryConfig);
218
-
219
- // Get enabled libraries
220
- const enabledLibs = StandardLibraryManager.getEnabledLibraries();
221
-
222
- // Get libraries by category
223
- const chartingLibs = StandardLibraryManager.getLibrariesByCategory('charting');
224
- const uiLibs = StandardLibraryManager.getLibrariesByCategory('ui');
225
-
226
- // Get component libraries (excludes runtime-only libraries)
227
- const componentLibs = StandardLibraryManager.getComponentLibraries();
228
-
229
- // Reset to default configuration
230
- StandardLibraryManager.resetToDefault();
231
- ```
232
-
233
- ### Library Categories
234
-
235
- Libraries are organized into categories:
236
- - **`runtime`**: Core runtime libraries (React, ReactDOM, Babel) - not exposed to components
237
- - **`ui`**: UI component libraries (Ant Design, React Bootstrap)
238
- - **`charting`**: Data visualization libraries (Chart.js, D3.js)
239
- - **`utility`**: Utility libraries (Lodash, Day.js)
240
-
241
- ### Runtime-Only Libraries
242
-
243
- Libraries marked with `isRuntimeOnly: true` are used by the runtime infrastructure but not exposed to generated components. This includes React, ReactDOM, and Babel.
244
-
245
- ### Using the Component
246
-
247
- ```typescript
248
- // Get React context (provided by your environment)
249
- const React = window.React; // or require('react')
250
- const ReactDOM = window.ReactDOM; // or require('react-dom')
251
-
252
- // Create runtime context with loaded libraries
253
- const context = {
254
- React,
255
- ReactDOM,
256
- libraries: result.libraries // From LibraryLoader
257
- };
258
-
259
- // Get the compiled component
260
- const MyComponent = runtime.registry.get('MyComponent', 'MyNamespace');
261
-
262
- // Execute the component factory
263
- const componentObject = MyComponent(context);
264
-
265
- // The componentObject contains the React component and method accessors
266
- const { component, print, refresh, getCurrentDataState, isDirty } = componentObject;
267
-
268
- // Render with props
269
- const props = {
270
- data: { name: 'World' },
271
- userState: {},
272
- callbacks: {
273
- OpenEntityRecord: (entityName, key) => console.log('Open entity:', entityName),
274
- RegisterMethod: (methodName, handler) => {
275
- // Component will register its methods here
276
- }
277
- }
278
- };
279
-
280
- React.createElement(component, props);
281
- ```
282
-
283
- ## Component Methods System
284
-
285
- ### Overview
286
-
287
- Components can expose methods that allow containers to interact with them beyond just passing props. This enables scenarios like:
288
- - AI agents introspecting component state
289
- - Containers checking if components have unsaved changes
290
- - Programmatic validation and reset operations
291
- - Custom business logic exposed by components
292
-
293
- ### How Components Register Methods
294
-
295
- Components register their methods during initialization using the `RegisterMethod` callback:
296
-
297
- ```typescript
298
- function MyComponent({ callbacks, data, userState }) {
299
- const [currentData, setCurrentData] = React.useState(data);
300
- const [isDirty, setIsDirty] = React.useState(false);
301
-
302
- // Register methods on mount
303
- React.useEffect(() => {
304
- if (callbacks?.RegisterMethod) {
305
- // Register standard methods
306
- callbacks.RegisterMethod('getCurrentDataState', () => {
307
- return currentData;
308
- });
309
-
310
- callbacks.RegisterMethod('isDirty', () => {
311
- return isDirty;
312
- });
313
-
314
- callbacks.RegisterMethod('reset', () => {
315
- setCurrentData(data);
316
- setIsDirty(false);
317
- });
318
-
319
- callbacks.RegisterMethod('validate', () => {
320
- // Custom validation logic
321
- if (!currentData.name) {
322
- return { valid: false, errors: ['Name is required'] };
323
- }
324
- return true;
325
- });
326
-
327
- // Register custom methods
328
- callbacks.RegisterMethod('exportToCSV', () => {
329
- // Custom export logic
330
- return convertToCSV(currentData);
331
- });
332
- }
333
- }, [callbacks, currentData, isDirty]);
334
-
335
- return (
336
- <div>
337
- {/* Component UI */}
338
- </div>
339
- );
340
- }
341
- ```
342
-
343
- ### Standard Methods
344
-
345
- The ComponentObject interface defines standard methods that components can optionally implement:
346
-
347
- - **`getCurrentDataState()`**: Returns the current data being displayed
348
- - **`getDataStateHistory()`**: Returns an array of timestamped state changes
349
- - **`validate()`**: Validates the component state
350
- - **`isDirty()`**: Checks if there are unsaved changes
351
- - **`reset()`**: Resets the component to initial state
352
- - **`scrollTo(target)`**: Scrolls to a specific element
353
- - **`focus(target)`**: Sets focus to an element
354
- - **`print()`**: Prints the component content
355
- - **`refresh()`**: Refreshes the component data
356
-
357
- ### Using Component Methods
358
-
359
- After compilation, the ComponentObject provides typed access to standard methods:
360
-
361
- ```typescript
362
- // Compile the component
363
- const result = await compiler.compile({
364
- componentName: 'MyComponent',
365
- componentCode: componentCode
122
+ // Register a library for dependency resolution
123
+ LibraryRegistry.register({
124
+ name: 'recharts',
125
+ module: rechartsModule,
126
+ version: '2.x'
366
127
  });
367
128
 
368
- // Get the component object
369
- const componentObject = result.component.component(context);
370
-
371
- // Call standard methods directly (type-safe)
372
- const currentData = componentObject.getCurrentDataState();
373
- const isDirty = componentObject.isDirty();
374
- const validationResult = componentObject.validate();
375
-
376
- if (isDirty) {
377
- componentObject.reset();
378
- }
379
-
380
- // Call custom methods via invokeMethod
381
- if (componentObject.hasMethod('exportToCSV')) {
382
- const csvData = componentObject.invokeMethod('exportToCSV');
383
- }
384
- ```
385
-
386
- ### Method Availability
387
-
388
- All methods are optional. The runtime provides sensible defaults when methods aren't registered:
389
-
390
- - `getCurrentDataState()` returns `undefined`
391
- - `getDataStateHistory()` returns `[]`
392
- - `isDirty()` returns `false`
393
- - `validate()` returns `true`
394
- - Other methods perform no operation if not implemented
395
-
396
- ### Integration with Angular
397
-
398
- The Angular wrapper (`@memberjunction/ng-react`) provides strongly-typed access to all standard methods:
399
-
400
- ```typescript
401
- export class MyDashboard {
402
- @ViewChild(MJReactComponent) reactComponent!: MJReactComponent;
403
-
404
- checkComponentState() {
405
- // Standard methods have full TypeScript support
406
- if (this.reactComponent.isDirty()) {
407
- const data = this.reactComponent.getCurrentDataState();
408
- console.log('Component has unsaved changes:', data);
409
- }
410
-
411
- // Validate before saving
412
- const validation = this.reactComponent.validate();
413
- if (validation === true || validation.valid) {
414
- // Save data...
415
- }
416
-
417
- // Custom methods
418
- if (this.reactComponent.hasMethod('generateReport')) {
419
- const report = this.reactComponent.invokeMethod('generateReport', options);
420
- }
421
- }
422
- }
423
- ```
424
-
425
- ### Method Declaration in Component Spec
426
-
427
- Components can declare their supported methods in the ComponentSpec for discovery:
428
-
429
- ```typescript
430
- const componentSpec = {
431
- name: 'MyComponent',
432
- code: '...',
433
- methods: [
434
- {
435
- name: 'getCurrentDataState',
436
- category: 'standard',
437
- description: 'Returns current component data',
438
- returnType: 'DataState | undefined'
439
- },
440
- {
441
- name: 'exportToExcel',
442
- category: 'custom',
443
- description: 'Exports data to Excel format',
444
- parameters: [{
445
- name: 'options',
446
- type: '{includeHeaders?: boolean, sheetName?: string}',
447
- required: false
448
- }],
449
- returnType: 'Promise<Blob>'
450
- }
451
- ]
452
- };
453
- ```
454
-
455
- ## Advanced Features
456
-
457
- ### Component Hierarchies
458
-
459
- ```typescript
460
- const parentSpec = {
461
- componentName: 'ParentComponent',
462
- componentCode: '...',
463
- childComponents: [
464
- {
465
- componentName: 'ChildComponent1',
466
- componentCode: '...'
467
- },
468
- {
469
- componentName: 'ChildComponent2',
470
- componentCode: '...'
471
- }
472
- ]
473
- };
474
-
475
- // Resolve all components in hierarchy
476
- const components = runtime.resolver.resolveComponents(parentSpec);
129
+ // Load libraries dynamically
130
+ const loader = new LibraryLoader();
131
+ const result = await loader.load({ name: 'recharts', url: cdnUrl });
477
132
  ```
478
133
 
479
134
  ### Error Boundaries
480
135
 
481
136
  ```typescript
482
- import { createErrorBoundary } from '@memberjunction/react-runtime';
483
-
484
- const ErrorBoundary = createErrorBoundary(React, {
485
- onError: (error, errorInfo) => {
486
- console.error('Component error:', error);
487
- },
488
- fallback: <div>Something went wrong</div>,
489
- recovery: 'retry'
490
- });
491
-
492
- // Wrap your component
493
- <ErrorBoundary>
494
- <YourComponent />
495
- </ErrorBoundary>
496
- ```
497
-
498
- ### Component Registry Management
499
-
500
- ```typescript
501
- // Check if component exists
502
- if (runtime.registry.has('MyComponent')) {
503
- // Get component with reference counting
504
- const component = runtime.registry.get('MyComponent');
505
-
506
- // Release when done
507
- runtime.registry.release('MyComponent');
508
- }
509
-
510
- // Get registry statistics
511
- const stats = runtime.registry.getStats();
512
- console.log(`Total components: ${stats.totalComponents}`);
513
-
514
- // Clean up unused components
515
- const removed = runtime.registry.cleanup();
516
- console.log(`Removed ${removed} unused components`);
517
- ```
518
-
519
- ### External Registry Components
520
-
521
- The React Runtime supports loading components from external registries through the `ComponentRegistryService`:
522
-
523
- ```typescript
524
- // Component specs can reference external registries
525
- const componentSpec = {
526
- name: 'DataGrid',
527
- location: 'registry',
528
- registry: 'MJ', // Registry name (globally unique)
529
- namespace: 'core/ui',
530
- version: 'latest',
531
- // ... other spec fields
532
- };
533
-
534
- // The runtime will:
535
- // 1. Look up the registry by name in ComponentRegistries
536
- // 2. Fetch the component via GraphQL/MJServer
537
- // 3. Calculate SHA-256 hash of the spec for cache validation
538
- // 4. Compile and cache the component
539
- ```
540
-
541
- #### GraphQL Client Configuration
542
-
543
- The `ComponentRegistryService` requires a GraphQL client for fetching from external registries. It supports two configuration approaches:
544
-
545
- 1. **Automatic Fallback** (Recommended): If no client is explicitly provided, the service automatically creates a `GraphQLComponentRegistryClient` using `Metadata.Provider`
546
- ```typescript
547
- // No explicit client needed - will create one from Metadata.Provider
548
- const registryService = ComponentRegistryService.getInstance(compiler, context);
549
- // The service will automatically:
550
- // 1. Check if a client was provided
551
- // 2. If not, dynamically import @memberjunction/graphql-dataprovider
552
- // 3. Create a GraphQLComponentRegistryClient with Metadata.Provider
553
- // 4. Cache and reuse this client for subsequent calls
554
- ```
555
-
556
- 2. **Explicit Client**: Provide a custom GraphQL client that implements `IComponentRegistryClient`
557
- ```typescript
558
- // Custom client implementation
559
- const customClient: IComponentRegistryClient = {
560
- GetRegistryComponent: async (params) => { /* ... */ }
561
- };
562
-
563
- // Pass during creation
564
- const registryService = ComponentRegistryService.getInstance(
565
- compiler, context, debug, customClient
566
- );
567
-
568
- // Or set later
569
- registryService.setGraphQLClient(customClient);
570
- ```
571
-
572
- The automatic fallback ensures external registry fetching works out-of-the-box in MemberJunction environments where `Metadata.Provider` is configured. The dynamic import approach allows the React runtime to function even when `@memberjunction/graphql-dataprovider` is not available.
573
-
574
- #### Component Caching with SHA-256 Validation
575
-
576
- The runtime uses SHA-256 hashing to ensure cached components are up-to-date:
577
-
578
- ```typescript
579
- // When fetching external components:
580
- // 1. Fetch spec from registry
581
- // 2. Calculate SHA-256 hash using Web Crypto API
582
- // 3. Compare with cached component's hash
583
- // 4. Recompile only if spec has changed
584
-
585
- // Note: Requires secure context (HTTPS or localhost)
586
- // Web Crypto API is used for consistent hashing across environments
587
- ```
588
-
589
- #### Registry Types
590
-
591
- - **Local Registry** (`registry` field undefined): Components stored in local database
592
- - **External Registry** (`registry` field defined): Components fetched from remote registries via MJServer
593
-
594
- ## Debug Configuration
595
-
596
- The React runtime includes comprehensive debug logging that can be controlled via environment configuration. This is useful for troubleshooting component loading, compilation, and runtime issues.
597
-
598
- ### Enabling Debug Mode
599
-
600
- Debug mode controls verbose console logging throughout the React runtime. When enabled, you'll see detailed information about:
601
-
602
- - Component compilation and registration
603
- - Library loading and initialization
604
- - Component lifecycle events
605
- - Method registration and invocation
606
- - Cache hits and misses
607
- - Performance metrics
608
-
609
- ### Configuration Methods
610
-
611
- #### Option 1: Angular Environment (Recommended for MJExplorer)
612
-
613
- Set the `DEBUG` flag in your Angular environment files:
614
-
615
- ```typescript
616
- // In environment.development.ts
617
- export const environment = {
618
- // ... other settings
619
- DEBUG: true // Enable detailed debug logging
620
- };
621
-
622
- // In main.ts (before Angular bootstraps)
623
- import { environment } from './environments/environment';
624
- import { ReactDebugConfig } from '@memberjunction/ng-react';
137
+ import { createErrorBoundary, withErrorBoundary } from '@memberjunction/react-runtime';
625
138
 
626
- ReactDebugConfig.setDebugMode(environment.DEBUG || false);
627
- ```
628
-
629
- #### Option 2: Window Global (For Quick Testing)
630
-
631
- Useful for temporarily enabling debug mode without changing code:
139
+ // Create an error boundary component
140
+ const ErrorBoundary = createErrorBoundary({
141
+ fallback: (error) => <div>Something went wrong: {error.message}</div>,
142
+ onError: (error) => logError(error)
143
+ });
632
144
 
633
- ```typescript
634
- // In browser console or before React components load
635
- (window as any).__MJ_REACT_DEBUG_MODE__ = true;
145
+ // Or wrap an existing component
146
+ const SafeComponent = withErrorBoundary(MyComponent, { fallback: ErrorFallback });
636
147
  ```
637
148
 
638
- #### Option 3: Direct API Call
639
-
640
- Call the API directly in your initialization code:
149
+ ### Prop Building
641
150
 
642
151
  ```typescript
643
- import { ReactDebugConfig } from '@memberjunction/ng-react';
152
+ import { buildComponentProps, normalizeCallbacks } from '@memberjunction/react-runtime';
644
153
 
645
- // Enable debug mode
646
- ReactDebugConfig.setDebugMode(true);
647
-
648
- // Check current debug mode
649
- const isDebug = ReactDebugConfig.getDebugMode();
154
+ const props = buildComponentProps(rawData, {
155
+ normalizeStyles: true,
156
+ normalizeCallbacks: true,
157
+ validateProps: true
158
+ });
650
159
  ```
651
160
 
652
- ### Debug Mode Priority
653
-
654
- The debug mode follows this priority order (highest to lowest):
655
-
656
- 1. **Window global override** (`__MJ_REACT_DEBUG_MODE__`) - Highest priority
657
- 2. **Static property** (set via `setDebugMode()` or environment) - Default
658
- 3. **Default** - `false` (debug disabled)
659
-
660
- ### When to Enable Debug Mode
161
+ ## Exported Modules
661
162
 
662
- **Enable in these scenarios:**
663
- - During local development
664
- - When troubleshooting component loading failures
665
- - When debugging component compilation errors
666
- - When investigating library dependency issues
667
- - When analyzing runtime performance
668
-
669
- ❌ **Disable in these scenarios:**
670
- - Production environments (cleaner console output)
671
- - Staging environments (unless actively debugging)
672
- - Performance profiling (reduces console overhead)
673
- - Automated testing (reduces noise in logs)
674
-
675
- ### Example Debug Output
676
-
677
- When debug mode is enabled, you'll see output like:
678
-
679
- ```
680
- [ReactRuntime] Compiling component: Dashboard
681
- [ReactRuntime] Component registered: Dashboard (namespace: analytics, version: 1.0.0)
682
- [ReactRuntime] Loading library: lodash (version: 4.17.21)
683
- [ReactRuntime] Library loaded successfully: lodash
684
- [ReactRuntime] Cache hit for component: Chart@analytics:1.0.0
685
- [ReactRuntime] Method registered: getCurrentDataState
686
- [ReactRuntime] Component hierarchy loaded: 5 components in 234ms
687
- ```
163
+ | Module | Key Exports |
164
+ |--------|-------------|
165
+ | **Compiler** | `ComponentCompiler`, `getBabelConfig`, `getJSXConfig` |
166
+ | **Registry** | `ComponentRegistry`, `ComponentResolver`, `ComponentRegistryService` |
167
+ | **Manager** | `ComponentManager`, `LoadOptions`, `LoadResult` |
168
+ | **Runtime** | `createErrorBoundary`, `buildComponentProps`, `ComponentHierarchyRegistrar`, `ReactRootManager` |
169
+ | **Utilities** | `LibraryLoader`, `LibraryRegistry`, `CacheManager`, `ResourceManager`, `StandardLibraries` |
688
170
 
689
171
  ## Configuration
690
172
 
691
- ### Compiler Configuration
692
-
693
173
  ```typescript
694
- const runtime = createReactRuntime(Babel, {
695
- compiler: {
696
- babel: {
697
- presets: ['react'],
698
- plugins: ['transform-optional-chaining']
174
+ const DEFAULT_CONFIGS = {
175
+ compiler: {
176
+ babel: { presets: ['react'], plugins: [] },
177
+ minify: false,
178
+ sourceMaps: false,
179
+ cache: true,
180
+ maxCacheSize: 100
699
181
  },
700
- minify: true,
701
- sourceMaps: true,
702
- cache: true,
703
- maxCacheSize: 200
704
- }
705
- });
706
- ```
707
-
708
- ### Registry Configuration
709
-
710
- ```typescript
711
- const runtime = createReactRuntime(Babel, {
712
- registry: {
713
- maxComponents: 500,
714
- cleanupInterval: 30000, // 30 seconds
715
- useLRU: true,
716
- enableNamespaces: true
717
- }
718
- });
182
+ registry: {
183
+ maxComponents: 1000,
184
+ cleanupInterval: 60000,
185
+ useLRU: true,
186
+ enableNamespaces: true
187
+ }
188
+ };
719
189
  ```
720
190
 
721
- ## API Reference
722
-
723
- ### Types
724
-
725
- - `CompileOptions` - Options for compiling components
726
- - `ComponentProps` - Standard props passed to components
727
- - `ComponentCallbacks` - Callback functions available to components
728
- - `RegistryEntry` - Registry entry with metadata
729
- - `LibraryConfiguration` - Configuration for external libraries
730
- - `ExternalLibraryConfig` - Individual library configuration
731
-
732
- ### Classes
733
-
734
- - `ComponentCompiler` - Compiles React components from source
735
- - `ComponentRegistry` - Manages compiled components
736
- - `ComponentResolver` - Resolves component dependencies
737
- - `StandardLibraryManager` - Manages library configurations
738
- - `LibraryLoader` - Loads external libraries dynamically
739
-
740
- ### Utilities
191
+ ## Build Outputs
741
192
 
742
- - `createErrorBoundary()` - Creates error boundary components
743
- - `buildComponentProps()` - Builds standardized component props
744
- - `wrapComponent()` - Wraps components with additional functionality
745
- - `createStandardLibraries()` - Creates standard library object from globals
193
+ - **CommonJS**: `dist/index.js` -- for Node.js and bundler environments
194
+ - **UMD**: `dist/umd/` -- for direct browser usage via script tags
746
195
 
747
- ## Best Practices
196
+ ## Dependencies
748
197
 
749
- 1. **Always Set Babel Instance**: Call `setBabelInstance()` before compiling
750
- 2. **Use Namespaces**: Organize components with namespaces
751
- 3. **Handle Errors**: Always check compilation results for errors
752
- 4. **Clean Up**: Use registry cleanup for long-running applications
753
- 5. **Type Safety**: Leverage TypeScript types for better development experience
754
- 6. **Library Management**: Configure only necessary libraries for security and performance
755
- 7. **Runtime Separation**: Keep runtime libraries separate from component libraries
198
+ | Package | Purpose |
199
+ |---------|---------|
200
+ | `@memberjunction/core` | Core MJ functionality |
201
+ | `@memberjunction/global` | MJGlobal utilities |
202
+ | `@memberjunction/interactive-component-types` | Component type definitions |
203
+ | `@memberjunction/core-entities` | Entity types |
204
+ | `@memberjunction/graphql-dataprovider` | GraphQL data access |
205
+ | `@babel/standalone` | Runtime JSX compilation |
206
+ | `rxjs` | Observable patterns |
756
207
 
757
208
  ## License
758
209
 
759
- See the main MemberJunction LICENSE file in the repository root.
210
+ ISC