@onlineapps/service-wrapper 2.0.12 → 2.0.15
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 +35 -9
- package/docs/MIGRATION_FROM_OPENAPI.md +309 -0
- package/docs/STANDARDS_OVERVIEW.md +226 -0
- package/examples/README.md +170 -0
- package/examples/cookbook-file-output.json +16 -0
- package/examples/cookbook-multi-step.json +23 -0
- package/examples/cookbook-single-operation.json +15 -0
- package/package.json +2 -2
- package/src/ServiceWrapper.js +99 -10
- /package/docs/{API_STRUCTURE_STANDARD.md → archived-2025-09-29/API_STRUCTURE_STANDARD.md} +0 -0
- /package/docs/{ARCHITECTURE_DECISION.md → archived-2025-09-29/ARCHITECTURE_DECISION.md} +0 -0
- /package/docs/{HANDLER_VS_HTTP_COMPARISON.md → archived-2025-09-29/HANDLER_VS_HTTP_COMPARISON.md} +0 -0
- /package/docs/{INSTALLATION_GUIDE.md → archived-2025-09-29/INSTALLATION_GUIDE.md} +0 -0
- /package/docs/{OPERATIONS_SCHEMA.md → archived-2025-09-29/OPERATIONS_SCHEMA.md} +0 -0
- /package/docs/{PERFORMANCE.md → archived-2025-09-29/PERFORMANCE.md} +0 -0
- /package/docs/{PROCESS_FLOWS.md → archived-2025-09-29/PROCESS_FLOWS.md} +0 -0
- /package/docs/{SERVICE_TESTING_STANDARD.md → archived-2025-09-29/SERVICE_TESTING_STANDARD.md} +0 -0
- /package/docs/{WRAPPER_ARCHITECTURE.md → archived-2025-09-29/WRAPPER_ARCHITECTURE.md} +0 -0
package/README.md
CHANGED
|
@@ -36,9 +36,9 @@ This package provides the infrastructure layer that sits between:
|
|
|
36
36
|
```
|
|
37
37
|
[RabbitMQ Message] → [Service Wrapper] → [Service API Call] → [Business Logic]
|
|
38
38
|
↑ ↑ ↑
|
|
39
|
-
This package
|
|
39
|
+
This package operations.json Your service
|
|
40
40
|
Handles all Defines API Pure business
|
|
41
|
-
infrastructure
|
|
41
|
+
infrastructure mapping logic only
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
## Usage
|
|
@@ -101,6 +101,22 @@ NO infrastructure code needed in your service!
|
|
|
101
101
|
|
|
102
102
|
## Configuration
|
|
103
103
|
|
|
104
|
+
### Environment Variable Parsing
|
|
105
|
+
|
|
106
|
+
**ServiceWrapper handles environment variable parsing directly** rather than delegating to a separate connector. This design decision is based on:
|
|
107
|
+
|
|
108
|
+
1. **Core Responsibility** - Configuration loading is fundamental to the wrapper's initialization, not a specialized concern
|
|
109
|
+
2. **Simplicity** - Avoids creating a separate connector for ~20 lines of parsing logic
|
|
110
|
+
3. **Performance** - Eliminates unnecessary abstraction layers for a trivial operation
|
|
111
|
+
4. **Universal Need** - All connectors (MQ, Registry, Cache) require parsed configuration
|
|
112
|
+
|
|
113
|
+
The wrapper parses `${VAR:default}` syntax in configuration files:
|
|
114
|
+
- `${RABBITMQ_URL}` - Uses environment variable, fails if not set
|
|
115
|
+
- `${PORT:3000}` - Uses environment variable, defaults to 3000 if not set
|
|
116
|
+
- `${ENABLED:true}` - Uses environment variable, defaults to true
|
|
117
|
+
|
|
118
|
+
This follows Docker/Kubernetes best practices for configuration management while keeping the wrapper architecture clean and focused.
|
|
119
|
+
|
|
104
120
|
### operations.json
|
|
105
121
|
```json
|
|
106
122
|
{
|
|
@@ -121,13 +137,23 @@ NO infrastructure code needed in your service!
|
|
|
121
137
|
{
|
|
122
138
|
"service": {
|
|
123
139
|
"name": "my-service",
|
|
124
|
-
"port": 3000
|
|
140
|
+
"port": "${PORT:3000}"
|
|
125
141
|
},
|
|
126
142
|
"wrapper": {
|
|
127
|
-
"mq": {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
143
|
+
"mq": {
|
|
144
|
+
"url": "${RABBITMQ_URL:amqp://localhost:5672}",
|
|
145
|
+
"enabled": "${MQ_ENABLED:true}"
|
|
146
|
+
},
|
|
147
|
+
"registry": {
|
|
148
|
+
"url": "${REGISTRY_URL}",
|
|
149
|
+
"enabled": "${REGISTRY_ENABLED:false}"
|
|
150
|
+
},
|
|
151
|
+
"monitoring": {
|
|
152
|
+
"enabled": "${MONITORING_ENABLED:true}"
|
|
153
|
+
},
|
|
154
|
+
"health": {
|
|
155
|
+
"endpoint": "/health"
|
|
156
|
+
}
|
|
131
157
|
}
|
|
132
158
|
}
|
|
133
159
|
```
|
|
@@ -154,9 +180,9 @@ NO infrastructure code needed in your service!
|
|
|
154
180
|
## Service Requirements
|
|
155
181
|
|
|
156
182
|
Your service needs:
|
|
157
|
-
1. **
|
|
183
|
+
1. **Operations specification** - Describes your API operations ([Operations Standard](/docs/OPERATIONS_STANDARD.md))
|
|
158
184
|
2. **Express app** - With business logic endpoints
|
|
159
|
-
3. **
|
|
185
|
+
3. **Configuration files** - Service config and environment variables
|
|
160
186
|
|
|
161
187
|
Your service should NOT have:
|
|
162
188
|
- Workflow processing code
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# Migration from OpenAPI to Operations Standard
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This guide helps you migrate existing services from OpenAPI specifications to the simpler Operations Standard.
|
|
5
|
+
|
|
6
|
+
## Why Migrate?
|
|
7
|
+
|
|
8
|
+
### Problems with OpenAPI
|
|
9
|
+
- **Over-engineered** for internal microservices
|
|
10
|
+
- **Complex** specification (100+ lines for simple operations)
|
|
11
|
+
- **Slow parsing** and validation overhead
|
|
12
|
+
- **Designed for external APIs**, not internal workflows
|
|
13
|
+
- **Poor fit** for cookbook-based orchestration
|
|
14
|
+
|
|
15
|
+
### Benefits of Operations Standard
|
|
16
|
+
- ✅ **Simpler** - 10x less configuration
|
|
17
|
+
- ✅ **Faster** - Direct JSON parsing
|
|
18
|
+
- ✅ **Focused** - Only what workflows need
|
|
19
|
+
- ✅ **Maintainable** - Easy to read and update
|
|
20
|
+
- ✅ **Flexible** - Can generate OpenAPI if needed later
|
|
21
|
+
|
|
22
|
+
## Migration Steps
|
|
23
|
+
|
|
24
|
+
### Step 1: Create operations.json
|
|
25
|
+
|
|
26
|
+
Transform your OpenAPI spec into Operations format:
|
|
27
|
+
|
|
28
|
+
#### Before (OpenAPI - openapi.json):
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"openapi": "3.0.0",
|
|
32
|
+
"info": {
|
|
33
|
+
"title": "Hello Service",
|
|
34
|
+
"version": "1.0.0"
|
|
35
|
+
},
|
|
36
|
+
"paths": {
|
|
37
|
+
"/api/good-day": {
|
|
38
|
+
"post": {
|
|
39
|
+
"operationId": "goodDay",
|
|
40
|
+
"summary": "Generate good day greeting",
|
|
41
|
+
"requestBody": {
|
|
42
|
+
"required": true,
|
|
43
|
+
"content": {
|
|
44
|
+
"application/json": {
|
|
45
|
+
"schema": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"required": ["name"],
|
|
48
|
+
"properties": {
|
|
49
|
+
"name": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"minLength": 1,
|
|
52
|
+
"maxLength": 100
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"responses": {
|
|
60
|
+
"200": {
|
|
61
|
+
"description": "Success",
|
|
62
|
+
"content": {
|
|
63
|
+
"application/json": {
|
|
64
|
+
"schema": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"properties": {
|
|
67
|
+
"message": { "type": "string" },
|
|
68
|
+
"timestamp": { "type": "string", "format": "date-time" }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### After (Operations - operations.json):
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"operations": {
|
|
85
|
+
"good-day": {
|
|
86
|
+
"description": "Generate good day greeting",
|
|
87
|
+
"endpoint": "/api/good-day",
|
|
88
|
+
"method": "POST",
|
|
89
|
+
"input": {
|
|
90
|
+
"name": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"required": true,
|
|
93
|
+
"minLength": 1,
|
|
94
|
+
"maxLength": 100
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"output": {
|
|
98
|
+
"message": { "type": "string" },
|
|
99
|
+
"timestamp": { "type": "datetime" }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Result**: ~50 lines → ~15 lines (70% reduction)
|
|
107
|
+
|
|
108
|
+
### Step 2: Update Directory Structure
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Old structure
|
|
112
|
+
services/my-service/
|
|
113
|
+
├── connector-config/
|
|
114
|
+
│ ├── manifest.json
|
|
115
|
+
│ └── openapi.json
|
|
116
|
+
|
|
117
|
+
# New structure
|
|
118
|
+
services/my-service/
|
|
119
|
+
├── conn-config/
|
|
120
|
+
│ ├── config.json
|
|
121
|
+
│ └── operations.json
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Move configuration:
|
|
125
|
+
```bash
|
|
126
|
+
cd services/my-service
|
|
127
|
+
mkdir -p conn-config
|
|
128
|
+
|
|
129
|
+
# If you have existing config, merge it
|
|
130
|
+
mv connector-config/manifest.json conn-config/config.json
|
|
131
|
+
|
|
132
|
+
# Create operations.json (see conversion below)
|
|
133
|
+
# Delete old directory
|
|
134
|
+
rm -rf connector-config/
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Step 3: Convert OpenAPI to Operations
|
|
138
|
+
|
|
139
|
+
Use this mapping:
|
|
140
|
+
|
|
141
|
+
| OpenAPI | Operations |
|
|
142
|
+
|---------|-----------|
|
|
143
|
+
| `paths[path][method].operationId` | `operations[operation-name]` |
|
|
144
|
+
| `paths[path]` | `endpoint` |
|
|
145
|
+
| `requestBody.content.schema.properties` | `input` |
|
|
146
|
+
| `responses.200.content.schema.properties` | `output` |
|
|
147
|
+
| `schema.required[]` | `field.required: true` |
|
|
148
|
+
| `schema.minLength` | `minLength` |
|
|
149
|
+
| `schema.maxLength` | `maxLength` |
|
|
150
|
+
|
|
151
|
+
### Step 4: Update ServiceWrapper Initialization
|
|
152
|
+
|
|
153
|
+
#### Before:
|
|
154
|
+
```javascript
|
|
155
|
+
const { ServiceWrapper } = require('@onlineapps/service-wrapper');
|
|
156
|
+
const openApiSpec = require('./connector-config/openapi.json');
|
|
157
|
+
|
|
158
|
+
const wrapper = new ServiceWrapper({
|
|
159
|
+
app,
|
|
160
|
+
server,
|
|
161
|
+
config,
|
|
162
|
+
openApiSpec // OLD
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### After:
|
|
167
|
+
```javascript
|
|
168
|
+
const { ServiceWrapper } = require('@onlineapps/service-wrapper');
|
|
169
|
+
const operations = require('./conn-config/operations.json');
|
|
170
|
+
|
|
171
|
+
const wrapper = new ServiceWrapper({
|
|
172
|
+
app,
|
|
173
|
+
server,
|
|
174
|
+
config,
|
|
175
|
+
operations // NEW
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Step 5: Update Specification Endpoint
|
|
180
|
+
|
|
181
|
+
#### Before:
|
|
182
|
+
```javascript
|
|
183
|
+
app.get('/specification', (req, res) => {
|
|
184
|
+
const openapi = require('./connector-config/openapi.json');
|
|
185
|
+
res.json(openapi);
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### After:
|
|
190
|
+
```javascript
|
|
191
|
+
app.get('/specification', (req, res) => {
|
|
192
|
+
const operations = require('./conn-config/operations.json');
|
|
193
|
+
res.json(operations);
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Step 6: Update Documentation References
|
|
198
|
+
|
|
199
|
+
Update all references:
|
|
200
|
+
- `connector-config/` → `conn-config/`
|
|
201
|
+
- `openapi.json` → `operations.json`
|
|
202
|
+
- "OpenAPI specification" → "Operations specification"
|
|
203
|
+
|
|
204
|
+
Files to check:
|
|
205
|
+
- README.md
|
|
206
|
+
- docker-compose.yml comments
|
|
207
|
+
- Test files
|
|
208
|
+
- Documentation
|
|
209
|
+
|
|
210
|
+
### Step 7: Test
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
# 1. Check specification endpoint
|
|
214
|
+
curl http://localhost:PORT/specification | jq .
|
|
215
|
+
|
|
216
|
+
# 2. Verify operations are listed
|
|
217
|
+
curl http://localhost:PORT/specification | jq '.operations | keys'
|
|
218
|
+
|
|
219
|
+
# 3. Test each operation
|
|
220
|
+
curl -X POST http://localhost:PORT/api/good-day \
|
|
221
|
+
-H "Content-Type: application/json" \
|
|
222
|
+
-d '{"name": "Test"}'
|
|
223
|
+
|
|
224
|
+
# 4. Run test suite
|
|
225
|
+
npm test
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Conversion Script
|
|
229
|
+
|
|
230
|
+
You can use this Node.js script to help convert OpenAPI to Operations:
|
|
231
|
+
|
|
232
|
+
```javascript
|
|
233
|
+
// convert-openapi-to-operations.js
|
|
234
|
+
const fs = require('fs');
|
|
235
|
+
|
|
236
|
+
function convertOpenAPIToOperations(openapi) {
|
|
237
|
+
const operations = {};
|
|
238
|
+
|
|
239
|
+
Object.entries(openapi.paths || {}).forEach(([path, methods]) => {
|
|
240
|
+
Object.entries(methods).forEach(([method, spec]) => {
|
|
241
|
+
const operationId = spec.operationId || path.split('/').pop();
|
|
242
|
+
|
|
243
|
+
operations[operationId] = {
|
|
244
|
+
description: spec.summary || spec.description,
|
|
245
|
+
endpoint: path,
|
|
246
|
+
method: method.toUpperCase(),
|
|
247
|
+
input: convertSchema(spec.requestBody?.content?.['application/json']?.schema),
|
|
248
|
+
output: convertSchema(spec.responses?.['200']?.content?.['application/json']?.schema)
|
|
249
|
+
};
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
return { operations };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function convertSchema(schema) {
|
|
257
|
+
if (!schema?.properties) return {};
|
|
258
|
+
|
|
259
|
+
const result = {};
|
|
260
|
+
Object.entries(schema.properties).forEach(([name, prop]) => {
|
|
261
|
+
result[name] = {
|
|
262
|
+
type: prop.type,
|
|
263
|
+
required: schema.required?.includes(name) || false,
|
|
264
|
+
...(prop.minLength && { minLength: prop.minLength }),
|
|
265
|
+
...(prop.maxLength && { maxLength: prop.maxLength }),
|
|
266
|
+
...(prop.description && { description: prop.description })
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Usage
|
|
274
|
+
const openapi = require('./connector-config/openapi.json');
|
|
275
|
+
const operations = convertOpenAPIToOperations(openapi);
|
|
276
|
+
fs.writeFileSync('./conn-config/operations.json', JSON.stringify(operations, null, 2));
|
|
277
|
+
console.log('Converted to operations.json');
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Validation
|
|
281
|
+
|
|
282
|
+
After migration, verify:
|
|
283
|
+
|
|
284
|
+
- [ ] `conn-config/operations.json` exists
|
|
285
|
+
- [ ] All operations have required fields (description, endpoint, method)
|
|
286
|
+
- [ ] Input/output schemas defined
|
|
287
|
+
- [ ] ServiceWrapper initializes without errors
|
|
288
|
+
- [ ] `/specification` endpoint returns operations
|
|
289
|
+
- [ ] All API endpoints work
|
|
290
|
+
- [ ] Tests pass
|
|
291
|
+
- [ ] Documentation updated
|
|
292
|
+
|
|
293
|
+
## Rollback
|
|
294
|
+
|
|
295
|
+
If you need to rollback:
|
|
296
|
+
|
|
297
|
+
1. Keep old `connector-config/` directory temporarily
|
|
298
|
+
2. Restore old ServiceWrapper initialization
|
|
299
|
+
3. Update references back
|
|
300
|
+
|
|
301
|
+
## Need Help?
|
|
302
|
+
|
|
303
|
+
- See [Operations Standard](/docs/OPERATIONS_STANDARD.md)
|
|
304
|
+
- Check [hello-service](/services/hello-service/) as reference
|
|
305
|
+
- Review [Service Template](/docs/SERVICE_TEMPLATE.md)
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
*Operations Standard simplifies service definitions while maintaining all necessary functionality for workflow orchestration.*
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# Service Wrapper Standards Overview
|
|
2
|
+
|
|
3
|
+
## All Standards in One Place
|
|
4
|
+
|
|
5
|
+
This document provides a comprehensive overview of all standards related to Service Wrapper and microservice development.
|
|
6
|
+
|
|
7
|
+
## 📋 Standards Documents
|
|
8
|
+
|
|
9
|
+
### 1. API Structure Standard
|
|
10
|
+
**Location:** [API_STRUCTURE_STANDARD.md](./API_STRUCTURE_STANDARD.md)
|
|
11
|
+
|
|
12
|
+
**Key Points:**
|
|
13
|
+
- URL structure: `/`, `/health`, `/status`, `/specification`, `/api/*`
|
|
14
|
+
- Service discovery endpoint returns metadata
|
|
15
|
+
- Operations endpoint at `/specification`
|
|
16
|
+
- Business logic under `/api/*`
|
|
17
|
+
- No versioning in URLs
|
|
18
|
+
|
|
19
|
+
### 2. Operations Schema Standard
|
|
20
|
+
**Location:** [OPERATIONS_SCHEMA.md](./OPERATIONS_SCHEMA.md)
|
|
21
|
+
|
|
22
|
+
**Key Points:**
|
|
23
|
+
- Replaces OpenAPI for internal services
|
|
24
|
+
- Located in `/conn-config/operations.json`
|
|
25
|
+
- Defines input/output schemas for operations
|
|
26
|
+
- Direct mapping to Cookbook workflow steps
|
|
27
|
+
- Supports both internal and external APIs
|
|
28
|
+
|
|
29
|
+
### 3. Configuration Standard
|
|
30
|
+
**Location:** [CONFIGURATION_GUIDE.md](./CONFIGURATION_GUIDE.md)
|
|
31
|
+
|
|
32
|
+
**Key Points:**
|
|
33
|
+
- Configuration in `/conn-config/` directory
|
|
34
|
+
- Three files: `config.json`, `operations.json`, `middleware.json`
|
|
35
|
+
- Environment variable format: `${VAR:default}`
|
|
36
|
+
- Parsed by ServiceWrapper directly
|
|
37
|
+
|
|
38
|
+
### 4. Service Testing Standard
|
|
39
|
+
**Location:** [SERVICE_TESTING_STANDARD.md](./SERVICE_TESTING_STANDARD.md)
|
|
40
|
+
|
|
41
|
+
**Key Points:**
|
|
42
|
+
- Unit tests for business logic
|
|
43
|
+
- Component tests for connectors
|
|
44
|
+
- Integration tests for MQ flows
|
|
45
|
+
- E2E tests for complete workflows
|
|
46
|
+
- Mock strategies for external dependencies
|
|
47
|
+
|
|
48
|
+
### 5. Architecture Standards
|
|
49
|
+
**Location:** [FINAL_ARCHITECTURE.md](./FINAL_ARCHITECTURE.md)
|
|
50
|
+
|
|
51
|
+
**Key Points:**
|
|
52
|
+
- Single-process architecture
|
|
53
|
+
- ServiceWrapper as thin orchestration layer
|
|
54
|
+
- Specialized connectors for infrastructure
|
|
55
|
+
- HTTP-only communication pattern
|
|
56
|
+
- No direct handler calls
|
|
57
|
+
|
|
58
|
+
## 🏗️ Directory Structure Standards
|
|
59
|
+
|
|
60
|
+
### Service Directory Layout
|
|
61
|
+
```
|
|
62
|
+
service-name/
|
|
63
|
+
├── src/ # Business logic only
|
|
64
|
+
│ ├── app.js # Express application
|
|
65
|
+
│ └── routes/ # API endpoints
|
|
66
|
+
├── conn-config/ # Configuration
|
|
67
|
+
│ ├── config.json # Service configuration
|
|
68
|
+
│ ├── operations.json # Operations specification
|
|
69
|
+
│ └── middleware.json # Middleware configuration
|
|
70
|
+
├── test/ # Tests
|
|
71
|
+
│ ├── unit/ # Business logic tests
|
|
72
|
+
│ ├── integration/ # API tests
|
|
73
|
+
│ └── e2e/ # Full flow tests
|
|
74
|
+
├── index.js # Service entry point
|
|
75
|
+
├── Dockerfile # Container definition
|
|
76
|
+
└── package.json # Dependencies
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 🔌 Connector Standards
|
|
80
|
+
|
|
81
|
+
### Naming Convention
|
|
82
|
+
- Base connectors: `conn-base-*` (cache, monitoring)
|
|
83
|
+
- Infrastructure: `conn-infra-*` (mq, error-handler)
|
|
84
|
+
- Orchestration: `conn-orch-*` (registry, cookbook, orchestrator)
|
|
85
|
+
|
|
86
|
+
### Package Naming
|
|
87
|
+
- npm scope: `@onlineapps/`
|
|
88
|
+
- Full name: `@onlineapps/conn-{layer}-{function}`
|
|
89
|
+
|
|
90
|
+
## 📦 Package Standards
|
|
91
|
+
|
|
92
|
+
### Version Management
|
|
93
|
+
- Semantic versioning (MAJOR.MINOR.PATCH)
|
|
94
|
+
- Breaking changes increment MAJOR
|
|
95
|
+
- New features increment MINOR
|
|
96
|
+
- Bug fixes increment PATCH
|
|
97
|
+
|
|
98
|
+
### Publishing
|
|
99
|
+
- Always test locally before publishing
|
|
100
|
+
- Document breaking changes in changelog
|
|
101
|
+
- Update dependent packages after publishing
|
|
102
|
+
|
|
103
|
+
## 🔧 Environment Variables
|
|
104
|
+
|
|
105
|
+
### Standard Variables
|
|
106
|
+
```bash
|
|
107
|
+
# Service Configuration
|
|
108
|
+
PORT=3000 # Service port
|
|
109
|
+
NODE_ENV=production # Environment
|
|
110
|
+
|
|
111
|
+
# Infrastructure
|
|
112
|
+
RABBITMQ_URL=amqp://... # Message queue
|
|
113
|
+
REGISTRY_URL=http://... # Service registry
|
|
114
|
+
REDIS_URL=redis://... # Cache
|
|
115
|
+
|
|
116
|
+
# Features
|
|
117
|
+
MQ_ENABLED=true # Enable MQ integration
|
|
118
|
+
REGISTRY_ENABLED=true # Enable registry
|
|
119
|
+
MONITORING_ENABLED=true # Enable monitoring
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Format in config.json
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"port": "${PORT:3000}",
|
|
126
|
+
"rabbitmq": "${RABBITMQ_URL:amqp://localhost:5672}"
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 📝 Documentation Standards
|
|
131
|
+
|
|
132
|
+
### Required Documentation
|
|
133
|
+
1. **README.md** - Overview and usage
|
|
134
|
+
2. **API.md** - API documentation (auto-generated)
|
|
135
|
+
3. **Configuration docs** - In `/docs` directory
|
|
136
|
+
4. **Inline comments** - Only when necessary
|
|
137
|
+
|
|
138
|
+
### Documentation Style
|
|
139
|
+
- Clear, concise explanations
|
|
140
|
+
- Examples for complex concepts
|
|
141
|
+
- Links to related documents
|
|
142
|
+
- No duplication - reference source of truth
|
|
143
|
+
|
|
144
|
+
## 🚀 Deployment Standards
|
|
145
|
+
|
|
146
|
+
### Docker
|
|
147
|
+
- Alpine-based images for size
|
|
148
|
+
- Non-root user for security
|
|
149
|
+
- Health checks included
|
|
150
|
+
- Multi-stage builds for optimization
|
|
151
|
+
|
|
152
|
+
### Health Checks
|
|
153
|
+
- Endpoint at `/health`
|
|
154
|
+
- Returns `{"status": "healthy", "timestamp": "..."}`
|
|
155
|
+
- Checked every 30 seconds
|
|
156
|
+
- 3 retries before marking unhealthy
|
|
157
|
+
|
|
158
|
+
## 🔄 Workflow Standards
|
|
159
|
+
|
|
160
|
+
### Cookbook Integration
|
|
161
|
+
- Operations map to cookbook steps
|
|
162
|
+
- Context passed between steps
|
|
163
|
+
- Error handling with retries
|
|
164
|
+
- Dead letter queue for failures
|
|
165
|
+
|
|
166
|
+
### Message Queue
|
|
167
|
+
- Queue naming: `{service-name}.workflow`
|
|
168
|
+
- Prefetch limit: 10 messages
|
|
169
|
+
- Acknowledgment after processing
|
|
170
|
+
- Automatic retries with backoff
|
|
171
|
+
|
|
172
|
+
## ✅ Code Quality Standards
|
|
173
|
+
|
|
174
|
+
### Best Practices
|
|
175
|
+
- No hardcoded values
|
|
176
|
+
- Configuration from environment
|
|
177
|
+
- Proper error handling
|
|
178
|
+
- Logging at appropriate levels
|
|
179
|
+
- Clean separation of concerns
|
|
180
|
+
|
|
181
|
+
### What Services Should NOT Have
|
|
182
|
+
- Workflow processing code
|
|
183
|
+
- Message queue operations
|
|
184
|
+
- Service registration logic
|
|
185
|
+
- Infrastructure connectors
|
|
186
|
+
- Environment variable parsing
|
|
187
|
+
|
|
188
|
+
## 📊 Monitoring Standards
|
|
189
|
+
|
|
190
|
+
### Metrics
|
|
191
|
+
- Request count and duration
|
|
192
|
+
- Error rates
|
|
193
|
+
- Queue depth
|
|
194
|
+
- Memory usage
|
|
195
|
+
- CPU utilization
|
|
196
|
+
|
|
197
|
+
### Logging
|
|
198
|
+
- Structured JSON logs
|
|
199
|
+
- Correlation IDs for tracing
|
|
200
|
+
- Appropriate log levels
|
|
201
|
+
- No sensitive data in logs
|
|
202
|
+
|
|
203
|
+
## 🔐 Security Standards
|
|
204
|
+
|
|
205
|
+
### Configuration
|
|
206
|
+
- Secrets from environment only
|
|
207
|
+
- No credentials in code
|
|
208
|
+
- No credentials in config files
|
|
209
|
+
- Secure defaults
|
|
210
|
+
|
|
211
|
+
### Communication
|
|
212
|
+
- HTTPS in production
|
|
213
|
+
- Authentication tokens
|
|
214
|
+
- Request validation
|
|
215
|
+
- Rate limiting
|
|
216
|
+
|
|
217
|
+
## 📚 References
|
|
218
|
+
|
|
219
|
+
- [Installation Guide](./INSTALLATION_GUIDE.md) - How to set up services
|
|
220
|
+
- [Performance Guide](./PERFORMANCE.md) - Optimization tips
|
|
221
|
+
- [Process Flows](./PROCESS_FLOWS.md) - How everything works together
|
|
222
|
+
- [Architecture Decision](./ARCHITECTURE_DECISION.md) - Why these choices
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
*This document serves as the single source of truth for all Service Wrapper standards.*
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Service Wrapper - Cookbook Examples
|
|
2
|
+
|
|
3
|
+
Example cookbooks demonstrating service-wrapper workflow orchestration.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
These examples show how cookbooks are processed by service-wrapper to orchestrate operations across microservices.
|
|
8
|
+
|
|
9
|
+
## Example Cookbooks
|
|
10
|
+
|
|
11
|
+
### [cookbook-single-operation.json](cookbook-single-operation.json)
|
|
12
|
+
Basic single-operation workflow.
|
|
13
|
+
|
|
14
|
+
**Features:**
|
|
15
|
+
- Single service call
|
|
16
|
+
- Simple string output
|
|
17
|
+
- Direct JSON response
|
|
18
|
+
|
|
19
|
+
**Expected Output:**
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"message": "Good day, Igor! Welcome to our service!",
|
|
23
|
+
"timestamp": "2025-09-30T12:00:00Z",
|
|
24
|
+
"service": "hello-service"
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
### [cookbook-file-output.json](cookbook-file-output.json)
|
|
31
|
+
Operation generating file output stored in MinIO.
|
|
32
|
+
|
|
33
|
+
**Features:**
|
|
34
|
+
- File generation (HTML/Markdown)
|
|
35
|
+
- MinIO storage reference
|
|
36
|
+
- `outputRef` for file retrieval
|
|
37
|
+
|
|
38
|
+
**Expected Output:**
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"certificate": "<!DOCTYPE html>...",
|
|
42
|
+
"outputRef": "cert-7d4e8f9a.html",
|
|
43
|
+
"timestamp": "2025-09-30T12:00:00Z"
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### [cookbook-multi-step.json](cookbook-multi-step.json)
|
|
50
|
+
Multi-step workflow orchestration.
|
|
51
|
+
|
|
52
|
+
**Features:**
|
|
53
|
+
- Sequential step execution
|
|
54
|
+
- Output passing between steps
|
|
55
|
+
- Multiple operations in one workflow
|
|
56
|
+
|
|
57
|
+
**Expected Output:**
|
|
58
|
+
Each step output accumulated in final workflow result.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Testing Workflows
|
|
63
|
+
|
|
64
|
+
### Via API Gateway (Production-like)
|
|
65
|
+
```bash
|
|
66
|
+
curl -X POST http://localhost:33000/workflow \
|
|
67
|
+
-H "Content-Type: application/json" \
|
|
68
|
+
-d @examples/cookbook-single-operation.json
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Direct Service Test (Development)
|
|
72
|
+
```bash
|
|
73
|
+
# Test hello-service directly
|
|
74
|
+
curl -X POST http://localhost:33199/api/good-day \
|
|
75
|
+
-H "Content-Type: application/json" \
|
|
76
|
+
-d '{"name": "Igor"}'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Via RabbitMQ (Integration Test)
|
|
80
|
+
```bash
|
|
81
|
+
# Publish directly to workflow queue
|
|
82
|
+
node scripts/publish-workflow.js examples/cookbook-single-operation.json
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Workflow Response Format
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"workflowId": "wf-2025-09-30-abc123",
|
|
90
|
+
"status": "completed",
|
|
91
|
+
"startTime": "2025-09-30T12:00:00Z",
|
|
92
|
+
"endTime": "2025-09-30T12:00:01Z",
|
|
93
|
+
"steps": [
|
|
94
|
+
{
|
|
95
|
+
"service": "hello-service",
|
|
96
|
+
"operation": "good-day",
|
|
97
|
+
"input": {...},
|
|
98
|
+
"output": {...},
|
|
99
|
+
"processingTime": 45,
|
|
100
|
+
"status": "success"
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Creating Custom Cookbooks
|
|
107
|
+
|
|
108
|
+
### 1. Define Operation in Service
|
|
109
|
+
|
|
110
|
+
Add to `conn-config/operations.json`:
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"my-operation": {
|
|
114
|
+
"description": "Custom operation",
|
|
115
|
+
"endpoint": "/api/my-operation",
|
|
116
|
+
"method": "POST",
|
|
117
|
+
"input": {
|
|
118
|
+
"param1": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"required": true
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"output": {
|
|
124
|
+
"result": {
|
|
125
|
+
"type": "string"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 2. Implement Service Endpoint
|
|
133
|
+
|
|
134
|
+
In service `src/routes/api.routes.js`:
|
|
135
|
+
```javascript
|
|
136
|
+
router.post('/my-operation', (req, res) => {
|
|
137
|
+
const { param1 } = req.body;
|
|
138
|
+
// Business logic
|
|
139
|
+
res.json({ result: "..." });
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 3. Create Cookbook
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"cookbook": {
|
|
148
|
+
"version": "1.0",
|
|
149
|
+
"steps": [{
|
|
150
|
+
"service": "your-service",
|
|
151
|
+
"operation": "my-operation",
|
|
152
|
+
"input": { "param1": "value" }
|
|
153
|
+
}]
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 4. Test
|
|
159
|
+
```bash
|
|
160
|
+
curl -X POST http://localhost:33000/workflow \
|
|
161
|
+
-H "Content-Type: application/json" \
|
|
162
|
+
-d @your-cookbook.json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Related Documentation
|
|
166
|
+
|
|
167
|
+
- [Operations Standard](/docs/OPERATIONS_STANDARD.md)
|
|
168
|
+
- [Service Wrapper Configuration](../docs/CONFIGURATION_GUIDE.md)
|
|
169
|
+
- [Workflow Module](/docs/modules/workflow.md)
|
|
170
|
+
- [Hello Service Examples](/services/hello-service/docs/WORKFLOW_EXECUTION.md)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cookbook": {
|
|
3
|
+
"version": "1.0",
|
|
4
|
+
"description": "Operation with file output (MinIO storage)",
|
|
5
|
+
"steps": [
|
|
6
|
+
{
|
|
7
|
+
"service": "hello-service",
|
|
8
|
+
"operation": "hello-cert",
|
|
9
|
+
"input": {
|
|
10
|
+
"name": "Igor",
|
|
11
|
+
"format": "html"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cookbook": {
|
|
3
|
+
"version": "1.0",
|
|
4
|
+
"description": "Multi-step workflow across services",
|
|
5
|
+
"steps": [
|
|
6
|
+
{
|
|
7
|
+
"service": "hello-service",
|
|
8
|
+
"operation": "good-day",
|
|
9
|
+
"input": {
|
|
10
|
+
"name": "Igor"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"service": "hello-service",
|
|
15
|
+
"operation": "hello-cert",
|
|
16
|
+
"input": {
|
|
17
|
+
"name": "Igor",
|
|
18
|
+
"format": "md"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/service-wrapper",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.15",
|
|
4
4
|
"description": "Thin orchestration layer for microservices - delegates all infrastructure concerns to specialized connectors",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@onlineapps/conn-orch-api-mapper": "^1.0.0",
|
|
32
32
|
"@onlineapps/conn-orch-cookbook": "^2.0.0",
|
|
33
33
|
"@onlineapps/conn-orch-orchestrator": "^1.0.1",
|
|
34
|
-
"@onlineapps/conn-orch-registry": "^1.1.
|
|
34
|
+
"@onlineapps/conn-orch-registry": "^1.1.12",
|
|
35
35
|
"@onlineapps/monitoring-core": "^1.0.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
package/src/ServiceWrapper.js
CHANGED
|
@@ -82,10 +82,25 @@ class ServiceWrapper {
|
|
|
82
82
|
_processConfig(config) {
|
|
83
83
|
// Replace environment variables in config
|
|
84
84
|
const processValue = (value) => {
|
|
85
|
-
if (typeof value === 'string'
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
if (typeof value === 'string') {
|
|
86
|
+
// Support ${VAR:default} format anywhere in string
|
|
87
|
+
return value.replace(/\$\{([^:}]+)(?::([^}]*))?\}/g, (match, varName, defaultValue) => {
|
|
88
|
+
const envValue = process.env[varName];
|
|
89
|
+
|
|
90
|
+
// Use env value if exists (even if empty string)
|
|
91
|
+
if (envValue !== undefined) {
|
|
92
|
+
return envValue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Use default if provided
|
|
96
|
+
if (defaultValue !== undefined) {
|
|
97
|
+
return defaultValue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Log warning and return empty string
|
|
101
|
+
console.warn(`[ServiceWrapper] Missing env variable: ${varName}`);
|
|
102
|
+
return '';
|
|
103
|
+
});
|
|
89
104
|
}
|
|
90
105
|
return value;
|
|
91
106
|
};
|
|
@@ -96,6 +111,10 @@ class ServiceWrapper {
|
|
|
96
111
|
for (const [key, value] of Object.entries(obj)) {
|
|
97
112
|
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
98
113
|
result[key] = processObject(value);
|
|
114
|
+
} else if (Array.isArray(value)) {
|
|
115
|
+
result[key] = value.map(item =>
|
|
116
|
+
typeof item === 'object' && item !== null ? processObject(item) : processValue(item)
|
|
117
|
+
);
|
|
99
118
|
} else {
|
|
100
119
|
result[key] = processValue(value);
|
|
101
120
|
}
|
|
@@ -146,9 +165,10 @@ class ServiceWrapper {
|
|
|
146
165
|
}
|
|
147
166
|
|
|
148
167
|
// 6. Initialize orchestrator for workflow processing
|
|
168
|
+
// NOTE: Orchestrator is prepared but workflow listeners will be started
|
|
169
|
+
// ONLY after receiving certificate from Registry (see _initializeRegistry)
|
|
149
170
|
if (this.mqClient) {
|
|
150
171
|
await this._initializeOrchestrator();
|
|
151
|
-
await this._subscribeToQueues();
|
|
152
172
|
}
|
|
153
173
|
|
|
154
174
|
this.isInitialized = true;
|
|
@@ -240,10 +260,15 @@ class ServiceWrapper {
|
|
|
240
260
|
amqpUrl: mqUrl,
|
|
241
261
|
serviceName: serviceName,
|
|
242
262
|
version: this.config.service?.version || '1.0.0',
|
|
263
|
+
registryQueue: 'registry.register', // Use correct queue name
|
|
243
264
|
registryUrl: registryUrl,
|
|
244
265
|
logger: this.logger || console
|
|
245
266
|
});
|
|
246
267
|
|
|
268
|
+
// Initialize registry client connection
|
|
269
|
+
await this.registryClient.init();
|
|
270
|
+
console.log('Registry client initialized');
|
|
271
|
+
|
|
247
272
|
// Register service
|
|
248
273
|
const serviceInfo = {
|
|
249
274
|
name: serviceName,
|
|
@@ -255,11 +280,39 @@ class ServiceWrapper {
|
|
|
255
280
|
}
|
|
256
281
|
};
|
|
257
282
|
|
|
258
|
-
|
|
259
|
-
|
|
283
|
+
// Validation proof is handled by RegistryClient.register() - no need to load here
|
|
284
|
+
// RegistryClient loads proof during init() and includes it in registration message
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
const registrationResult = await this.registryClient.register(serviceInfo);
|
|
288
|
+
console.log(`Service registered: ${serviceName}`, registrationResult);
|
|
289
|
+
|
|
290
|
+
// Store certificate if received
|
|
291
|
+
if (registrationResult.certificate) {
|
|
292
|
+
this.certificate = registrationResult.certificate;
|
|
293
|
+
console.log(`✓ Certificate received: ${registrationResult.certificate.certificateId}`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// CRITICAL: Only start workflow listeners after successful registration with certificate
|
|
297
|
+
if (registrationResult.success && registrationResult.certificate) {
|
|
298
|
+
console.log('✓ Certificate validated, starting workflow listeners...');
|
|
299
|
+
await this._startWorkflowListeners();
|
|
300
|
+
} else {
|
|
301
|
+
console.warn('⚠ Registration succeeded but no certificate received - workflow listeners NOT started');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
} catch (error) {
|
|
305
|
+
console.error(`Service registration failed: ${error.message}`);
|
|
306
|
+
throw new Error(`Failed to register service ${serviceName}: ${error.message}`);
|
|
307
|
+
}
|
|
260
308
|
|
|
261
309
|
// Start heartbeat
|
|
262
|
-
|
|
310
|
+
// Priority: ENV variable > config > default (10s)
|
|
311
|
+
const heartbeatInterval = parseInt(process.env.HEARTBEAT_INTERVAL) ||
|
|
312
|
+
this.config.wrapper?.registry?.heartbeatInterval ||
|
|
313
|
+
10000;
|
|
314
|
+
console.log(`[ServiceWrapper] Heartbeat interval set to ${heartbeatInterval}ms`);
|
|
315
|
+
|
|
263
316
|
this.heartbeatTimer = setInterval(async () => {
|
|
264
317
|
try {
|
|
265
318
|
await this.registryClient.sendHeartbeat(serviceName);
|
|
@@ -366,10 +419,11 @@ class ServiceWrapper {
|
|
|
366
419
|
}
|
|
367
420
|
|
|
368
421
|
/**
|
|
369
|
-
*
|
|
422
|
+
* Start workflow listeners - ONLY called after receiving certificate from Registry
|
|
423
|
+
* CRITICAL: Unregistered services MUST NOT consume workflow messages
|
|
370
424
|
* @private
|
|
371
425
|
*/
|
|
372
|
-
async
|
|
426
|
+
async _startWorkflowListeners() {
|
|
373
427
|
const serviceName = this.config.service?.name || 'unnamed-service';
|
|
374
428
|
|
|
375
429
|
// Subscribe to workflow.init queue (for all services)
|
|
@@ -539,6 +593,41 @@ class ServiceWrapper {
|
|
|
539
593
|
}
|
|
540
594
|
}
|
|
541
595
|
|
|
596
|
+
/**
|
|
597
|
+
* Load validation proof from file
|
|
598
|
+
* @returns {Object|null} Validation proof or null if not found
|
|
599
|
+
*/
|
|
600
|
+
loadValidationProof() {
|
|
601
|
+
const fs = require('fs');
|
|
602
|
+
const path = require('path');
|
|
603
|
+
|
|
604
|
+
try {
|
|
605
|
+
// Look for .validation-proof.json in service root (process.cwd())
|
|
606
|
+
const proofPath = path.join(process.cwd(), '.validation-proof.json');
|
|
607
|
+
|
|
608
|
+
if (!fs.existsSync(proofPath)) {
|
|
609
|
+
console.warn('Validation proof not found:', proofPath);
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const proofData = fs.readFileSync(proofPath, 'utf8');
|
|
614
|
+
const proof = JSON.parse(proofData);
|
|
615
|
+
|
|
616
|
+
// Basic validation
|
|
617
|
+
if (!proof.validationProof || !proof.validationData) {
|
|
618
|
+
console.warn('Invalid validation proof format');
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
console.log('✓ Validation proof loaded:', proof.validationProof.substring(0, 16) + '...');
|
|
623
|
+
return proof;
|
|
624
|
+
|
|
625
|
+
} catch (error) {
|
|
626
|
+
console.warn('Failed to load validation proof:', error.message);
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
542
631
|
/**
|
|
543
632
|
* Get wrapper status
|
|
544
633
|
*/
|
|
File without changes
|
|
File without changes
|
/package/docs/{HANDLER_VS_HTTP_COMPARISON.md → archived-2025-09-29/HANDLER_VS_HTTP_COMPARISON.md}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/docs/{SERVICE_TESTING_STANDARD.md → archived-2025-09-29/SERVICE_TESTING_STANDARD.md}
RENAMED
|
File without changes
|
|
File without changes
|