@onlineapps/service-wrapper 3.2.0 → 3.3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/service-wrapper",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "Thin orchestration layer for microservices - delegates all infrastructure concerns to specialized connectors",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -25,18 +25,18 @@
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
27
  "@onlineapps/conn-base-cache": "1.0.9",
28
- "@onlineapps/conn-base-monitoring": "1.0.12",
28
+ "@onlineapps/conn-base-monitoring": "1.0.13",
29
29
  "@onlineapps/conn-base-state": "1.0.1",
30
- "@onlineapps/conn-infra-error-handler": "1.0.11",
30
+ "@onlineapps/conn-infra-error-handler": "1.0.12",
31
31
  "@onlineapps/conn-infra-mq": "1.1.70",
32
32
  "@onlineapps/conn-infra-secrets": "1.0.0",
33
33
  "@onlineapps/conn-orch-cookbook": "2.1.3",
34
- "@onlineapps/conn-orch-orchestrator": "2.1.0",
35
- "@onlineapps/conn-orch-registry": "1.2.1",
36
- "@onlineapps/conn-orch-validator": "3.1.2",
37
- "@onlineapps/monitoring-core": "1.0.23",
34
+ "@onlineapps/conn-orch-orchestrator": "2.1.1",
35
+ "@onlineapps/conn-orch-registry": "1.2.2",
36
+ "@onlineapps/conn-orch-validator": "3.2.0",
37
+ "@onlineapps/monitoring-core": "1.0.24",
38
38
  "@onlineapps/runtime-config": "1.0.2",
39
- "@onlineapps/service-common": "1.1.2",
39
+ "@onlineapps/service-common": "1.1.3",
40
40
  "ajv": "8.17.1",
41
41
  "ajv-formats": "3.0.1"
42
42
  },
@@ -802,11 +802,11 @@ class ServiceWrapper {
802
802
  // Konfigurace se načítá v konstruktoru, takže tady jen logujeme
803
803
  this._logPhase('0.1', 'Configuration Load', 'PASSED', null, Date.now() - startTime);
804
804
 
805
- // FÁZE 0.1b: v2 Operations-Routes alignment validator retired.
806
- // v3 replaces it with _validateOperationsHandlerAlignment(), invoked
807
- // from the v3 pipeline block below (after connectors are ready).
805
+ // FÁZE 0.1b: Operations→Handler alignment is validated by
806
+ // _validateOperationsHandlerAlignment(), invoked from the v3 pipeline
807
+ // block below (after connectors are ready).
808
808
  // See RFC api/docs/architecture/biz-service-invocation-model.md §5.9 step 5.
809
- this._logPhase('0.1b', 'Operations-Routes Alignment', 'SKIPPED', null, 0);
809
+ this._logPhase('0.1b', 'Operations-Handler Alignment', 'DEFERRED', null, 0);
810
810
 
811
811
  // FÁZE 0.15: Runtime integration connector contract (health + readiness source of truth)
812
812
  if (this.config.wrapper?.health?.enabled !== false) {
@@ -1482,7 +1482,7 @@ class ServiceWrapper {
1482
1482
 
1483
1483
  /**
1484
1484
  * Initialize secrets connector (SecretBox). Resolves ctx.secrets.get(ref) by
1485
- * reading the encrypted Redis projection written by api_secrets and decrypting
1485
+ * reading the encrypted Redis projection written by biz-meta and decrypting
1486
1486
  * in-process. Redis reuses wrapper.cache.url (shared api_node_cache); the AES
1487
1487
  * master key is required from env (no fallback).
1488
1488
  * @private
@@ -1791,9 +1791,8 @@ class ServiceWrapper {
1791
1791
 
1792
1792
  const timeout = this.config.wrapper?.timeout || 30000;
1793
1793
 
1794
- // P1.5b: orchestrator now dispatches via service-wrapper.invokeOperation
1795
- // (intra-service handler dispatch, RFC §5.9, §5.10) — no more HTTP-loopback
1796
- // through the retired conn-orch-api-mapper.
1794
+ // Orchestrator dispatches via service-wrapper.invokeOperation
1795
+ // (intra-service handler dispatch, RFC §5.9, §5.10).
1797
1796
  this.orchestrator = OrchestratorConnector.create({
1798
1797
  mqClient: this.mqClient,
1799
1798
  registryClient: this.registryClient,
@@ -2017,8 +2016,7 @@ class ServiceWrapper {
2017
2016
  // Process based on message type
2018
2017
  let result;
2019
2018
  if (message.operation && this.operations?.operations?.[message.operation]) {
2020
- // v3 handler-registry dispatch. Replaces v2 apiMapper.callAPI /
2021
- // _executeOperation HTTP-loopback path.
2019
+ // v3 handler-registry dispatch.
2022
2020
  // See api/docs/architecture/biz-service-invocation-model.md §5.9.
2023
2021
  result = await this._dispatchViaInvokeOperation(message, message.operation);
2024
2022
  } else if (message.step?.operation && this.operations?.operations?.[message.step.operation]) {
@@ -2121,9 +2119,8 @@ class ServiceWrapper {
2121
2119
 
2122
2120
  /**
2123
2121
  * Adapt an MQ workflow message into the v3 invokeOperation contract,
2124
- * invoke it, and unwrap the returned envelope to match the previous
2125
- * `_executeOperation` return contract (result object on success, throws
2126
- * on error) so the surrounding _processWorkflowMessage flow is unchanged.
2122
+ * invoke it, and unwrap the returned envelope (result object on success,
2123
+ * throws on error) so the surrounding _processWorkflowMessage flow is unchanged.
2127
2124
  *
2128
2125
  * See RFC §5.9 for envelope shape.
2129
2126
  * @private
@@ -2165,82 +2162,6 @@ class ServiceWrapper {
2165
2162
  throw err;
2166
2163
  }
2167
2164
 
2168
- /**
2169
- * Execute operation by calling HTTP endpoint (v2 HTTP-loopback path).
2170
- *
2171
- * v2 legacy — no longer invoked from the MQ dispatch path; retained for
2172
- * any downstream caller until P5.1 cleanup.
2173
- *
2174
- * @private
2175
- */
2176
- async _executeOperation(operationName, input) {
2177
- const operation = this.operations?.operations?.[operationName];
2178
- if (!operation) {
2179
- throw new Error(`Unknown operation: ${operationName}`);
2180
- }
2181
-
2182
- const serviceUrl = this.config.service?.url;
2183
- if (!serviceUrl) {
2184
- throw new Error('[ServiceWrapper] Missing configuration - service.url is required');
2185
- }
2186
-
2187
- const url = new URL(operation.endpoint, serviceUrl).toString();
2188
- const method = operation.method || 'POST';
2189
-
2190
- this.logger?.debug(`Executing operation ${operationName} via ${method} ${url}`);
2191
-
2192
- // Make HTTP request using fetch (Node.js 18+) or http module
2193
- const http = require('http');
2194
-
2195
- return new Promise((resolve, reject) => {
2196
- const postData = JSON.stringify(input);
2197
-
2198
- const target = new URL(url);
2199
- const targetPort = target.port
2200
- ? parseInt(target.port, 10)
2201
- : (target.protocol === 'https:' ? 443 : 80);
2202
-
2203
- const options = {
2204
- hostname: target.hostname,
2205
- port: targetPort,
2206
- path: `${target.pathname}${target.search}`,
2207
- method: method,
2208
- headers: {
2209
- 'Content-Type': 'application/json',
2210
- 'Content-Length': Buffer.byteLength(postData)
2211
- }
2212
- };
2213
-
2214
- const req = http.request(options, (res) => {
2215
- let data = '';
2216
-
2217
- res.on('data', (chunk) => {
2218
- data += chunk;
2219
- });
2220
-
2221
- res.on('end', () => {
2222
- try {
2223
- const result = JSON.parse(data);
2224
- if (res.statusCode >= 200 && res.statusCode < 300) {
2225
- resolve(result);
2226
- } else {
2227
- reject(new Error(`Operation failed: ${result.error || data}`));
2228
- }
2229
- } catch (error) {
2230
- reject(new Error(`Invalid response: ${data}`));
2231
- }
2232
- });
2233
- });
2234
-
2235
- req.on('error', (error) => {
2236
- reject(error);
2237
- });
2238
-
2239
- req.write(postData);
2240
- req.end();
2241
- });
2242
- }
2243
-
2244
2165
  /**
2245
2166
  * Extract all registered Express routes from the app router stack.
2246
2167
  * Handles nested Router instances (e.g. app.use('/api', router)).
@@ -2347,7 +2268,7 @@ class ServiceWrapper {
2347
2268
  }
2348
2269
 
2349
2270
  /**
2350
- * v3 alignment validator replaces _validateOperationsRouteAlignment.
2271
+ * Alignment validator for the v3 handler-registry.
2351
2272
  *
2352
2273
  * Cross-checks that every operation declared in operations.json has a
2353
2274
  * registered handler in HandlerRegistry, and that no handler is registered
@@ -2434,103 +2355,6 @@ class ServiceWrapper {
2434
2355
  }
2435
2356
  }
2436
2357
 
2437
- /**
2438
- * Bidirectional validation: operations.json <-> Express routes.
2439
- * Check A: every declared operation has a matching Express route.
2440
- * Check B: every /api/* route has a matching operation or is in _exclude.
2441
- *
2442
- * v2 legacy — no longer invoked; scheduled for removal in P5.1.
2443
- *
2444
- * @private
2445
- * @throws {Error} Permanent error listing all mismatches
2446
- */
2447
- _validateOperationsRouteAlignment() {
2448
- const ops = this.operations?.operations || {};
2449
- const excludeEntries = this.operations?._exclude || [];
2450
- const expressRoutes = this._extractExpressRoutes();
2451
-
2452
- const routeSet = new Set(
2453
- expressRoutes.map(r => `${r.method}:${r.path}`)
2454
- );
2455
-
2456
- const errors = [];
2457
-
2458
- // Check A: every operation endpoint+method must exist as an Express route
2459
- for (const [opName, opDef] of Object.entries(ops)) {
2460
- const method = (opDef.method || 'POST').toUpperCase();
2461
- const endpoint = opDef.endpoint;
2462
- if (!endpoint) continue;
2463
-
2464
- if (!routeSet.has(`${method}:${endpoint}`)) {
2465
- errors.push(`Operation '${opName}' endpoint ${method} ${endpoint} not found in Express routes`);
2466
- }
2467
- }
2468
-
2469
- // Build lookup: endpoint -> set of operation methods
2470
- const opEndpoints = new Map();
2471
- for (const opDef of Object.values(ops)) {
2472
- const method = (opDef.method || 'POST').toUpperCase();
2473
- const endpoint = opDef.endpoint;
2474
- if (!endpoint) continue;
2475
- if (!opEndpoints.has(endpoint)) opEndpoints.set(endpoint, new Set());
2476
- opEndpoints.get(endpoint).add(method);
2477
- }
2478
-
2479
- // Build _exclude lookup
2480
- const excludeSet = new Set(
2481
- excludeEntries.map(e => `${(e.method || 'GET').toUpperCase()}:${e.endpoint}`)
2482
- );
2483
-
2484
- // Check B: every /api/* route must have a matching operation or be in _exclude
2485
- for (const route of expressRoutes) {
2486
- if (!route.path.startsWith('/api')) continue;
2487
-
2488
- const key = `${route.method}:${route.path}`;
2489
- if (excludeSet.has(key)) continue;
2490
-
2491
- const endpointOps = opEndpoints.get(route.path);
2492
- if (!endpointOps) {
2493
- errors.push(`Route ${route.method} ${route.path} has no matching operation in operations.json`);
2494
- continue;
2495
- }
2496
-
2497
- if (!endpointOps.has(route.method)) {
2498
- // Tolerate GET alongside a declared POST (common browser-test pattern)
2499
- if (route.method === 'GET' && endpointOps.size > 0) continue;
2500
- errors.push(`Route ${route.method} ${route.path} has no matching operation in operations.json`);
2501
- }
2502
- }
2503
-
2504
- // Extension 1: specificationEndpoint must match an Express route
2505
- // See: docs/standards/api-versioning-contract.md
2506
- const specEndpoint = this.config.service?.specificationEndpoint;
2507
- if (specEndpoint && !routeSet.has(`GET:${specEndpoint}`)) {
2508
- errors.push(
2509
- `specificationEndpoint '${specEndpoint}' not found in Express routes — ` +
2510
- `config claims this path but no GET route is registered`
2511
- );
2512
- }
2513
-
2514
- // Extension 3: operations.json endpoints under /api/ must be versioned
2515
- // See: docs/standards/api-versioning-contract.md
2516
- const versionPattern = /^\/api\/v\d+\//;
2517
- for (const [opName, opDef] of Object.entries(ops)) {
2518
- if (opDef.endpoint?.startsWith('/api') && !versionPattern.test(opDef.endpoint)) {
2519
- errors.push(
2520
- `Operation '${opName}' endpoint '${opDef.endpoint}' missing version — ` +
2521
- `expected /api/v{N}/... (see docs/standards/api-versioning-contract.md)`
2522
- );
2523
- }
2524
- }
2525
-
2526
- if (errors.length > 0) {
2527
- throw new Error(
2528
- `[ServiceWrapper] Operations-Routes alignment failed (${errors.length} issue${errors.length > 1 ? 's' : ''}):\n` +
2529
- errors.map(e => ` - ${e}`).join('\n')
2530
- );
2531
- }
2532
- }
2533
-
2534
2358
  /**
2535
2359
  * Delete local validation proof and trigger re-validation.
2536
2360
  * Called when Registry sends a revalidation request (infra version change).
package/src/index.js CHANGED
@@ -18,9 +18,10 @@ const runtimeCfg = require('./config');
18
18
  const pkg = require('../package.json');
19
19
  const { createTenantContextMiddleware } = require('./createTenantContextMiddleware');
20
20
 
21
- // Note: WorkflowProcessor and ApiCaller functionality has been moved to connectors:
22
- // - WorkflowProcessor -> @onlineapps/conn-orch-orchestrator
23
- // - ApiCaller -> @onlineapps/conn-orch-api-mapper
21
+ // Note: WorkflowProcessor functionality has been moved to
22
+ // @onlineapps/conn-orch-orchestrator. The former ApiCaller /
23
+ // @onlineapps/conn-orch-api-mapper was retired 2026-04-21 (ADR-0001);
24
+ // per-operation dispatch happens in-process via HandlerRegistry.
24
25
 
25
26
  // Note: HTTP logging middleware is now in each service's middlewares/httpLogging.js
26
27
  // It uses lazy logger access via lib/logger.js (set after wrapper.initialize())