@ea-lab/reactive-json-docs 0.2.1 → 0.4.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.
Files changed (31) hide show
  1. package/package.json +2 -2
  2. package/public/rjbuild/docs/advanced-concepts/data-mapping.md +76 -0
  3. package/public/rjbuild/docs/advanced-concepts/data-mapping.yaml +140 -0
  4. package/public/rjbuild/docs/advanced-concepts/data-processors.md +373 -0
  5. package/public/rjbuild/docs/advanced-concepts/data-processors.yaml +309 -0
  6. package/public/rjbuild/docs/advanced-concepts/index.md +9 -0
  7. package/public/rjbuild/docs/advanced-concepts/index.yaml +15 -0
  8. package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/component-development-guide-llm.md +2 -0
  9. package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/plugin-system.md +2 -0
  10. package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/plugin-system.yaml +2 -0
  11. package/public/rjbuild/docs/core/dataMapping/index.md +31 -0
  12. package/public/rjbuild/docs/core/dataMapping/index.yaml +24 -0
  13. package/public/rjbuild/docs/core/dataMapping/simpleMapping.md +131 -0
  14. package/public/rjbuild/docs/core/dataMapping/simpleMapping.yaml +376 -0
  15. package/public/rjbuild/docs/core/element/special/ReactiveJsonSubroot.md +4 -3
  16. package/public/rjbuild/docs/core/element/special/ReactiveJsonSubroot.yaml +114 -6
  17. package/public/rjbuild/docs/core/reaction/fetchData.md +136 -10
  18. package/public/rjbuild/docs/core/reaction/fetchData.yaml +222 -9
  19. package/public/rjbuild/docs/core/reaction/submitData.md +139 -4
  20. package/public/rjbuild/docs/core/reaction/submitData.yaml +50 -2
  21. package/public/rjbuild/docs/{getting-started.md → getting-started/index.md} +2 -2
  22. package/public/rjbuild/docs/{getting-started.yaml → getting-started/index.yaml} +4 -2
  23. package/public/rjbuild/docs/getting-started/rjbuild-structure.md +352 -0
  24. package/public/rjbuild/docs/getting-started/rjbuild-structure.yaml +415 -0
  25. package/public/rjbuild/docs/install.yaml +682 -681
  26. /package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/component-development.md +0 -0
  27. /package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/component-development.yaml +0 -0
  28. /package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/index.md +0 -0
  29. /package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/index.yaml +0 -0
  30. /package/public/rjbuild/docs/{template.md → getting-started/template.md} +0 -0
  31. /package/public/rjbuild/docs/{template.yaml → getting-started/template.yaml} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ea-lab/reactive-json-docs",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Complete documentation for Reactive-JSON - Components, examples and LLM-parsable guides",
5
5
  "main": "public/rjbuild/docs/index.yaml",
6
6
  "files": [
@@ -26,7 +26,7 @@
26
26
  "private": false,
27
27
  "devDependencies": {
28
28
  "@craco/craco": "^7.1.0",
29
- "@ea-lab/reactive-json": "^0.1.0",
29
+ "@ea-lab/reactive-json": "^0.3.1",
30
30
  "@ea-lab/reactive-json-chartjs": "^0.0.23",
31
31
  "@npmcli/fs": "^4.0.0",
32
32
  "@reduxjs/toolkit": "^2.6.1",
@@ -0,0 +1,76 @@
1
+ # Data Mapping System
2
+
3
+ Data Mapping is a powerful, extensible system in Reactive-JSON that allows you to selectively dispatch and transform response data from HTTP requests to specific locations in your application state.
4
+
5
+ ## System Architecture
6
+
7
+ The Data Mapping system operates **after** Data Processors have processed the response, providing a clean separation of concerns:
8
+
9
+ 1. **HTTP Request** → Server responds with raw data
10
+ 2. **Data Processors** → Transform/validate the raw response
11
+ 3. **Data Mapping** → Selectively dispatch transformed data to application state
12
+ 4. **Application State** → Updated with mapped data
13
+
14
+ ## Core Components
15
+
16
+ ### SimpleMapping (Core)
17
+
18
+ Reactive-JSON includes **simpleMapping** as a core data mapper, providing string-based mappings for common data dispatch scenarios:
19
+
20
+ - **Automatic Availability**: No additional configuration required
21
+ - **String-based Configuration**: YAML-friendly destination → source mappings
22
+ - **Error Handling**: Built-in fallback values and error recovery
23
+ - **Template Integration**: Full support for `~~.` and `~.` template references
24
+
25
+ **→ [View SimpleMapping Documentation & Examples](../core/dataMapping/simpleMapping)**
26
+
27
+ ## Custom Data Mappers
28
+
29
+ The Data Mapping system is extensible through custom mapper plugins. You can create specialized mappers for complex transformation logic, integration with external libraries, or domain-specific processing needs.
30
+
31
+ ### Creating Custom Mappers
32
+
33
+ ```javascript
34
+ const customMapper = ({config, responseData, globalDataContext, templateContext}) => {
35
+ // Custom mapping logic
36
+ const {updateData} = globalDataContext;
37
+
38
+ // Process config and apply transformations
39
+ updateData(transformedValue, "destination.path");
40
+ };
41
+
42
+ // Register in plugin system
43
+ const customPlugins = {
44
+ dataMapping: {
45
+ customMapper
46
+ }
47
+ };
48
+ ```
49
+
50
+ ### Integration with Core
51
+
52
+ Custom mappers are automatically merged with core mappers, allowing you to extend the system while keeping access to `simpleMapping`.
53
+
54
+ ## Usage Context
55
+
56
+ Data Mapping can be used with all HTTP-based data operations in Reactive-JSON:
57
+
58
+ - **fetchData & submitData reactions**: Process API responses selectively
59
+ - **additionalDataSources**: Map initial data loading responses
60
+ - **Combined with Data Processors**: Sequential processing pipeline
61
+
62
+ ## Best Practices
63
+
64
+ 1. **Use simpleMapping first**: It handles most common data dispatch scenarios
65
+ 2. **Combine with Data Processors**: Let processors handle validation/transformation, mappers handle dispatch
66
+ 3. **Template References**: Leverage `~~.` and `~.` for dynamic mappings
67
+ 4. **Error Handling**: Always configure `onErrorMap` for critical data paths
68
+ 5. **Performance**: Use `required: false` with `defaultValue` for optional fields
69
+
70
+ ## Related Documentation
71
+
72
+ - [SimpleMapping Examples & Configuration](../core/dataMapping/simpleMapping) - Complete guide to the core mapper
73
+ - [Data Processors](data-processors) - Pre-mapping data transformation
74
+ - [Plugin System](plugins/plugin-system) - Creating custom components
75
+ - [FetchData Reaction](../core/reaction/fetchData) - HTTP request handling
76
+ - [SubmitData Reaction](../core/reaction/submitData) - Form submission with data mapping
@@ -0,0 +1,140 @@
1
+ renderView:
2
+ - type: Markdown
3
+ content: |
4
+ # Data Mapping System
5
+
6
+ Data Mapping is a powerful, extensible system in Reactive-JSON that allows you to selectively dispatch and transform response data from HTTP requests to specific locations in your application state. This system provides fine-grained control over how incoming data is processed and stored after HTTP operations.
7
+
8
+ Data mapping occurs after the [Data Processors](data-processors) have processed the response.
9
+
10
+ ## System Architecture
11
+
12
+ The Data Mapping system processes HTTP response data to selectively dispatch it to application state:
13
+
14
+ 1. **HTTP Request** → Server responds with data
15
+ 2. **Data Mapping** → Selectively dispatch response data to application state
16
+ 3. **Application State** → Updated with mapped data
17
+
18
+ ## Core Components
19
+
20
+ ### SimpleMapping (Core)
21
+
22
+ Reactive-JSON includes **simpleMapping** as a core data mapper, providing string-based mappings for common data dispatch scenarios.
23
+
24
+ - **Automatic Availability**: No additional configuration required
25
+ - **String-based Configuration**: YAML-friendly destination → source mappings
26
+ - **Error Handling**: Built-in fallback values and error recovery
27
+ - **Template Integration**: Full support for `~~.` and `~.` template references
28
+
29
+ **→ [View SimpleMapping Documentation & Examples](../core/dataMapping/simpleMapping)**
30
+
31
+ ## Custom Data Mappers
32
+
33
+ The Data Mapping system is extensible through custom mapper plugins. You can create specialized mappers for complex transformation logic, integration with external libraries, or domain-specific processing needs.
34
+
35
+ ### Creating Custom Mappers
36
+
37
+ ```javascript
38
+ const customMapper = ({config, responseData, globalDataContext, templateContext}) => {
39
+ // Custom mapping logic
40
+ const {updateData} = globalDataContext;
41
+
42
+ // Process config and apply transformations
43
+ updateData(transformedValue, "destination.path");
44
+ };
45
+
46
+ // Register in plugin system
47
+ const customPlugins = {
48
+ dataMapping: {
49
+ customMapper
50
+ }
51
+ };
52
+ ```
53
+
54
+ ### Integration with Core
55
+
56
+ Custom mappers are automatically merged with core mappers, allowing you to extend the system while keeping access to `simpleMapping`:
57
+
58
+ ```yaml
59
+ dataMapping:
60
+ simpleMapping:
61
+ # Core mapper usage
62
+ stringMap:
63
+ "~~.profile.name": { value: "user.name" }
64
+ customMapper:
65
+ # Your custom mapper configuration
66
+ specialConfig: "value"
67
+ ```
68
+
69
+ ## Usage Context
70
+
71
+ Data Mapping can be used with all HTTP-based data operations in Reactive-JSON:
72
+
73
+ - type: Markdown
74
+ content: |
75
+ ### fetchData & submitData Reactions
76
+
77
+ ```yaml
78
+ - what: fetchData
79
+ url: "/api/user-profile"
80
+ updateOnlyData: true
81
+ dataMapping:
82
+ simpleMapping:
83
+ stringMap:
84
+ "~~.currentUser.name": { value: "user.name" }
85
+ "~~.currentUser.email": { value: "user.email" }
86
+ ```
87
+
88
+ ### additionalDataSources
89
+
90
+ ```yaml
91
+ additionalDataSource:
92
+ - src: "/api/initial-config"
93
+ blocking: true
94
+ dataMapping:
95
+ simpleMapping:
96
+ stringMap:
97
+ "~~.appConfig.theme": { value: "config.theme" }
98
+ "~~.appConfig.features": { value: "config.enabled_features" }
99
+ ```
100
+
101
+ ## Plugin System Integration
102
+
103
+ The Data Mapping system integrates with Reactive-JSON's plugin architecture:
104
+
105
+ ```javascript
106
+ import { mergeComponentCollections, ReactiveJsonRoot } from "@ea-lab/reactive-json";
107
+
108
+ const customPlugins = {
109
+ dataMapping: {
110
+ // Custom mappers
111
+ advancedMapper: myAdvancedMapper,
112
+ xmlMapper: myXmlMapper
113
+ }
114
+ };
115
+
116
+ // Core plugins (including simpleMapping) are automatically merged
117
+ const plugins = mergeComponentCollections([customPlugins]);
118
+
119
+ export const MyApp = (props) => (
120
+ <ReactiveJsonRoot {...props} plugins={plugins} />
121
+ );
122
+ ```
123
+
124
+ ## Best Practices
125
+
126
+ 1. **Use simpleMapping first**: It handles most common data dispatch scenarios
127
+ 2. **Template References**: Leverage `~~.` and `~.` for dynamic mappings
128
+ 3. **Error Handling**: Always configure `onErrorMap` for critical data paths
129
+ 4. **Performance**: Use `required: false` with `defaultValue` for optional fields
130
+
131
+ ## Related Documentation
132
+
133
+ - **[SimpleMapping Examples & Configuration](../core/dataMapping/simpleMapping)** - Complete guide to the core mapper
134
+ - **[Plugin System](plugins/plugin-system)** - Creating custom components
135
+ - **[FetchData Reaction](../core/reaction/fetchData)** - HTTP request handling
136
+ - **[SubmitData Reaction](../core/reaction/submitData)** - Form submission with data mapping
137
+
138
+ templates:
139
+
140
+ data:
@@ -0,0 +1,373 @@
1
+ # Data Processors
2
+
3
+ Data Processors are a powerful feature in Reactive-JSON that allow you to intercept and modify data received via `fetchData`, `submitData`, and `additionalDataSources`. This enables you to implement data transformation, validation, security filtering, and other data processing logic in a centralized and reusable way.
4
+
5
+ ## How Data Processors Work
6
+
7
+ When Reactive-JSON receives data from HTTP requests, it automatically passes the data through all registered Data Processors in order. Each processor:
8
+
9
+ 1. **Examines the request and response context** (URL, method, headers, status, etc.)
10
+ 2. **Receives the current data** being processed
11
+ 3. **Must return the data** - either transformed or unchanged
12
+ 4. **Chains with other processors** (processed data flows to the next processor)
13
+
14
+ ## Basic Structure
15
+
16
+ A Data Processor is a function that receives context and data, then **must return** the processed data:
17
+
18
+ ```javascript
19
+ const myDataProcessor = ({ requestContext, responseContext, dataToProcess, originalDataToProcess }) => {
20
+ // Check if we want to process this data
21
+ if (!requestContext.url.includes('/api/users')) {
22
+ return dataToProcess; // Return unchanged data
23
+ }
24
+
25
+ // Check response status
26
+ if (responseContext.status >= 400) {
27
+ return dataToProcess; // Return unchanged data
28
+ }
29
+
30
+ // Transform the data
31
+ const processedData = {
32
+ ...dataToProcess,
33
+ timestamp: new Date().toISOString(),
34
+ source: requestContext.url
35
+ };
36
+
37
+ return processedData; // Return transformed data
38
+ };
39
+ ```
40
+
41
+ ## Function Parameters
42
+
43
+ ### requestContext
44
+ Information about the HTTP request:
45
+ - `url`: The URL that was called
46
+ - `method`: HTTP method (GET, POST, etc.)
47
+ - `headers`: Request headers object
48
+ - `body`: Request body (for POST, PUT, etc.)
49
+
50
+ ### responseContext
51
+ Information about the HTTP response:
52
+ - `headers`: Response headers object
53
+ - `status`: HTTP status code (200, 404, etc.)
54
+ - `data`: Raw response data
55
+
56
+ ### dataToProcess
57
+ The data currently being processed. This may have been modified by previous Data Processors in the chain.
58
+
59
+ ### originalDataToProcess
60
+ The original data before any processing, useful for comparison or logging.
61
+
62
+ ## Plugin Registration
63
+
64
+ Data Processors are registered through the plugin system:
65
+
66
+ ```javascript
67
+ import { mergeComponentCollections } from "@ea-lab/reactive-json";
68
+
69
+ const myPlugins = {
70
+ element: {
71
+ // Your custom components
72
+ },
73
+ dataProcessor: {
74
+ "timestamp-processor": {
75
+ callback: timestampProcessor,
76
+ order: 0
77
+ },
78
+ "security-filter": {
79
+ callback: securityProcessor,
80
+ order: 10
81
+ }
82
+ }
83
+ };
84
+
85
+ export const MyReactiveJsonRoot = (props) => {
86
+ const plugins = mergeComponentCollections([myPlugins]);
87
+ return <ReactiveJsonRoot {...props} plugins={plugins} />;
88
+ };
89
+ ```
90
+
91
+ ### Plugin Structure
92
+ - **Key**: Unique identifier for the processor
93
+ - **callback**: The processor function
94
+ - **order**: Execution order (lower numbers run first)
95
+
96
+ ## Common Use Cases
97
+
98
+ ### Adding Timestamps
99
+
100
+ ```javascript
101
+ const timestampProcessor = ({ requestContext, responseContext, dataToProcess, originalDataToProcess }) => {
102
+ if (typeof dataToProcess !== 'object' || dataToProcess === null) {
103
+ return dataToProcess; // Return unchanged for non-objects
104
+ }
105
+
106
+ return {
107
+ ...dataToProcess,
108
+ __metadata: {
109
+ processedAt: new Date().toISOString(),
110
+ sourceUrl: requestContext.url,
111
+ responseStatus: responseContext.status
112
+ }
113
+ };
114
+ };
115
+ ```
116
+
117
+ ### Security Filtering
118
+
119
+ ```javascript
120
+ const securityProcessor = ({ requestContext, responseContext, dataToProcess, originalDataToProcess }) => {
121
+ // Only filter external API responses
122
+ if (!requestContext.url.includes('external-api')) {
123
+ return dataToProcess; // Return unchanged
124
+ }
125
+
126
+ if (typeof dataToProcess !== 'object' || dataToProcess === null) {
127
+ return dataToProcess; // Return unchanged for non-objects
128
+ }
129
+
130
+ // Remove sensitive fields
131
+ const sensitiveFields = ['password', 'secret', 'apiKey', 'token'];
132
+ const cleanedData = { ...dataToProcess };
133
+
134
+ sensitiveFields.forEach(field => {
135
+ if (cleanedData.hasOwnProperty(field)) {
136
+ delete cleanedData[field];
137
+ console.log(`🔒 Removed sensitive field: ${field}`);
138
+ }
139
+ });
140
+
141
+ return cleanedData;
142
+ };
143
+ ```
144
+
145
+ ### Data Format Transformation
146
+
147
+ ```javascript
148
+ const dateFormatProcessor = ({ requestContext, responseContext, dataToProcess, originalDataToProcess }) => {
149
+ if (typeof dataToProcess !== 'object' || dataToProcess === null) {
150
+ return dataToProcess; // Return unchanged for non-objects
151
+ }
152
+
153
+ const processedData = { ...dataToProcess };
154
+
155
+ // Convert DD/MM/YYYY dates to YYYY-MM-DD
156
+ Object.keys(processedData).forEach(key => {
157
+ if (typeof processedData[key] === 'string' && /^\d{2}\/\d{2}\/\d{4}$/.test(processedData[key])) {
158
+ const [day, month, year] = processedData[key].split('/');
159
+ processedData[key] = `${year}-${month}-${day}`;
160
+ }
161
+ });
162
+
163
+ return processedData;
164
+ };
165
+ ```
166
+
167
+ ### 4. Conditional Processing
168
+
169
+ ```javascript
170
+ const userDataProcessor = ({ requestContext, responseContext, dataToProcess, originalDataToProcess }) => {
171
+ // Only process successful user API responses
172
+ if (!requestContext.url.includes('/api/users') || responseContext.status !== 200) {
173
+ return dataToProcess; // Return unchanged
174
+ }
175
+
176
+ if (typeof dataToProcess !== 'object' || dataToProcess === null) {
177
+ return dataToProcess; // Return unchanged for non-objects
178
+ }
179
+
180
+ // Add user-specific processing
181
+ return {
182
+ ...dataToProcess,
183
+ fullName: `${dataToProcess.firstName} ${dataToProcess.lastName}`,
184
+ isActive: dataToProcess.status === 'active',
185
+ permissions: dataToProcess.role === 'admin' ? ['read', 'write', 'delete'] : ['read']
186
+ };
187
+ };
188
+ ```
189
+
190
+ ## RjBuild vs Data Processing
191
+
192
+ The system automatically detects whether the response is a complete RjBuild or just data:
193
+
194
+ - **Complete RjBuild**: Processors only modify the `data` section
195
+ - **Data only**: Processors modify the entire response
196
+
197
+ This happens automatically based on the `updateOnlyData` parameter in `fetchData`/`submitData`.
198
+
199
+ ## Execution Context
200
+
201
+ ### With `fetchData`/`submitData`
202
+ ```yaml
203
+ - type: button
204
+ content: "Load User Data"
205
+ actions:
206
+ - what: fetchData
207
+ on: click
208
+ url: "/api/users/123"
209
+ refreshAppOnResponse: true
210
+ updateOnlyData: false # The response is expected to be a complete RjBuild.
211
+ # When the response is a complete RjBuild, the processors will receive the data only.
212
+ ```
213
+
214
+ ### With `additionalDataSources`
215
+ ```yaml
216
+ additionalDataSource:
217
+ - src: "/api/config"
218
+ path: "~~.config"
219
+ # Processors will automatically process this data
220
+ ```
221
+
222
+ ## Best Practices
223
+
224
+ ### Always Return Data
225
+ Data Processors **must always return data**. To skip processing, return the original data:
226
+
227
+ ```javascript
228
+ const myProcessor = ({ requestContext, responseContext, dataToProcess, originalDataToProcess }) => {
229
+ // Skip processing for certain conditions
230
+ if (!requestContext.url.includes('/api/')) {
231
+ return dataToProcess; // Return unchanged
232
+ }
233
+ if (responseContext.status >= 400) {
234
+ return dataToProcess; // Return unchanged
235
+ }
236
+ if (typeof dataToProcess !== 'object') {
237
+ return dataToProcess; // Return unchanged
238
+ }
239
+
240
+ // Process and return transformed data
241
+ return { ...dataToProcess, processed: true };
242
+ };
243
+ ```
244
+
245
+ ### Immutable Updates
246
+ Always clone data before modifying:
247
+
248
+ ```javascript
249
+ const processedData = { ...dataToProcess }; // Shallow clone
250
+ // Or for deep cloning, use JSON.parse(JSON.stringify(...)):
251
+ const processedData = JSON.parse(JSON.stringify(dataToProcess));
252
+ // Or with lodash:
253
+ const processedData = _.cloneDeep(dataToProcess);
254
+ ```
255
+
256
+ ### Error Handling
257
+ The system automatically catches errors, but you can add your own:
258
+
259
+ ```javascript
260
+ const safeProcessor = ({ requestContext, responseContext, dataToProcess, originalDataToProcess }) => {
261
+ try {
262
+ // Your processing logic
263
+ return processedData;
264
+ } catch (error) {
265
+ console.error('Processing failed:', error);
266
+ return dataToProcess; // Return original data on error
267
+ }
268
+ };
269
+ ```
270
+
271
+ ### 4. Descriptive IDs and Ordering
272
+ Use clear names and logical ordering:
273
+
274
+ ```javascript
275
+ const plugins = {
276
+ dataProcessor: {
277
+ "01-timestamp": { callback: timestampProcessor, order: 0 },
278
+ "02-security": { callback: securityProcessor, order: 10 },
279
+ "03-formatting": { callback: formatProcessor, order: 20 }
280
+ }
281
+ };
282
+ ```
283
+
284
+ ## Debugging
285
+
286
+ ### Logging
287
+ Add console logs to understand the flow:
288
+
289
+ ```javascript
290
+ const debugProcessor = ({ requestContext, responseContext, dataToProcess, originalDataToProcess }) => {
291
+ console.log('🔍 Processing:', requestContext.url);
292
+ console.log('📊 Status:', responseContext.status);
293
+ console.log('📥 Data keys:', Object.keys(dataToProcess));
294
+
295
+ // Your processing logic
296
+ const processedData = { ...dataToProcess, debug: true };
297
+
298
+ console.log('📤 Processed keys:', Object.keys(processedData));
299
+ return processedData;
300
+ };
301
+ ```
302
+
303
+ ### Browser DevTools
304
+ The system automatically logs processor errors to the console, making debugging straightforward.
305
+
306
+ ## Complete Example
307
+
308
+ Here's a comprehensive example showing multiple processors working together:
309
+
310
+ ```javascript
311
+ import { mergeComponentCollections } from "@ea-lab/reactive-json";
312
+
313
+ // Utility processors
314
+ const timestampProcessor = ({ requestContext, responseContext, dataToProcess, originalDataToProcess }) => {
315
+ if (typeof dataToProcess !== 'object' || dataToProcess === null) {
316
+ return dataToProcess;
317
+ }
318
+
319
+ return {
320
+ ...dataToProcess,
321
+ __processed: {
322
+ timestamp: new Date().toISOString(),
323
+ url: requestContext.url,
324
+ status: responseContext.status
325
+ }
326
+ };
327
+ };
328
+
329
+ const securityProcessor = ({ requestContext, responseContext, dataToProcess, originalDataToProcess }) => {
330
+ if (!requestContext.url.includes('external')) {
331
+ return dataToProcess;
332
+ }
333
+ if (typeof dataToProcess !== 'object' || dataToProcess === null) {
334
+ return dataToProcess;
335
+ }
336
+
337
+ const cleaned = { ...dataToProcess };
338
+ ['password', 'secret', 'token'].forEach(field => delete cleaned[field]);
339
+ return cleaned;
340
+ };
341
+
342
+ const userDataProcessor = ({ requestContext, responseContext, dataToProcess, originalDataToProcess }) => {
343
+ if (!requestContext.url.includes('/users')) {
344
+ return dataToProcess;
345
+ }
346
+ if (typeof dataToProcess !== 'object' || dataToProcess === null) {
347
+ return dataToProcess;
348
+ }
349
+
350
+ return {
351
+ ...dataToProcess,
352
+ displayName: `${dataToProcess.firstName} ${dataToProcess.lastName}`,
353
+ isOnline: dataToProcess.lastSeen &&
354
+ (Date.now() - new Date(dataToProcess.lastSeen).getTime()) < 300000 // 5 minutes
355
+ };
356
+ };
357
+
358
+ const myPlugins = {
359
+ element: {
360
+ // Your components
361
+ },
362
+ dataProcessor: {
363
+ "timestamp": { callback: timestampProcessor, order: 0 },
364
+ "security": { callback: securityProcessor, order: 5 },
365
+ "user-enhancement": { callback: userDataProcessor, order: 10 }
366
+ }
367
+ };
368
+
369
+ export const MyApp = (props) => {
370
+ const plugins = mergeComponentCollections([myPlugins]);
371
+ return <ReactiveJsonRoot {...props} plugins={plugins} />;
372
+ };
373
+ ```