@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.
- package/package.json +2 -2
- package/public/rjbuild/docs/advanced-concepts/data-mapping.md +76 -0
- package/public/rjbuild/docs/advanced-concepts/data-mapping.yaml +140 -0
- package/public/rjbuild/docs/advanced-concepts/data-processors.md +373 -0
- package/public/rjbuild/docs/advanced-concepts/data-processors.yaml +309 -0
- package/public/rjbuild/docs/advanced-concepts/index.md +9 -0
- package/public/rjbuild/docs/advanced-concepts/index.yaml +15 -0
- package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/component-development-guide-llm.md +2 -0
- package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/plugin-system.md +2 -0
- package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/plugin-system.yaml +2 -0
- package/public/rjbuild/docs/core/dataMapping/index.md +31 -0
- package/public/rjbuild/docs/core/dataMapping/index.yaml +24 -0
- package/public/rjbuild/docs/core/dataMapping/simpleMapping.md +131 -0
- package/public/rjbuild/docs/core/dataMapping/simpleMapping.yaml +376 -0
- package/public/rjbuild/docs/core/element/special/ReactiveJsonSubroot.md +4 -3
- package/public/rjbuild/docs/core/element/special/ReactiveJsonSubroot.yaml +114 -6
- package/public/rjbuild/docs/core/reaction/fetchData.md +136 -10
- package/public/rjbuild/docs/core/reaction/fetchData.yaml +222 -9
- package/public/rjbuild/docs/core/reaction/submitData.md +139 -4
- package/public/rjbuild/docs/core/reaction/submitData.yaml +50 -2
- package/public/rjbuild/docs/{getting-started.md → getting-started/index.md} +2 -2
- package/public/rjbuild/docs/{getting-started.yaml → getting-started/index.yaml} +4 -2
- package/public/rjbuild/docs/getting-started/rjbuild-structure.md +352 -0
- package/public/rjbuild/docs/getting-started/rjbuild-structure.yaml +415 -0
- package/public/rjbuild/docs/install.yaml +682 -681
- /package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/component-development.md +0 -0
- /package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/component-development.yaml +0 -0
- /package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/index.md +0 -0
- /package/public/rjbuild/docs/{extend → advanced-concepts/plugins}/index.yaml +0 -0
- /package/public/rjbuild/docs/{template.md → getting-started/template.md} +0 -0
- /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.
|
|
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
|
|
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
|
+
```
|