@onlineapps/service-wrapper 2.1.116 → 2.1.118

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 (2) hide show
  1. package/package.json +2 -2
  2. package/src/ConfigLoader.js +61 -24
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/service-wrapper",
3
- "version": "2.1.116",
3
+ "version": "2.1.118",
4
4
  "description": "Thin orchestration layer for microservices - delegates all infrastructure concerns to specialized connectors",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -32,7 +32,7 @@
32
32
  "@onlineapps/conn-orch-cookbook": "2.0.35",
33
33
  "@onlineapps/conn-orch-orchestrator": "1.0.103",
34
34
  "@onlineapps/conn-orch-registry": "1.1.52",
35
- "@onlineapps/conn-orch-validator": "2.0.29",
35
+ "@onlineapps/conn-orch-validator": "2.0.30",
36
36
  "@onlineapps/monitoring-core": "1.0.21",
37
37
  "@onlineapps/service-common": "1.0.17",
38
38
  "@onlineapps/runtime-config": "1.0.2"
@@ -27,26 +27,48 @@ const runtimeCfg = require('./config');
27
27
  /**
28
28
  * CENTRAL REGISTRY: All configuration files used by business services
29
29
  * If you add a new config file, add it here!
30
+ *
31
+ * Path resolution order (first found wins):
32
+ * 1. config/service/ (new standard)
33
+ * 2. conn-config/ (legacy, backward compat)
30
34
  */
31
35
  const CONFIG_REGISTRY = {
32
36
  SERVICE_CONFIG: {
33
- path: 'conn-config/config.json',
37
+ paths: ['config/service/config.json', 'conn-config/config.json'],
34
38
  required: true,
35
39
  description: 'Service metadata and wrapper configuration'
36
40
  },
37
41
  OPERATIONS: {
38
- path: 'conn-config/operations.json',
42
+ paths: ['config/service/operations.json', 'conn-config/operations.json'],
39
43
  required: true,
40
44
  description: 'Operations specification (API contract)'
41
45
  },
42
46
  VALIDATION_PROOF: {
43
- path: 'conn-runtime/validation-proof.json',
47
+ paths: ['config/runtime/validation-proof.json', 'conn-runtime/validation-proof.json'],
44
48
  required: false,
45
49
  description: 'Pre-validation proof from cookbook tests'
46
50
  }
47
51
  };
48
52
 
49
53
  class ConfigLoader {
54
+ /**
55
+ * Resolve a config file from an ordered list of candidate paths.
56
+ * Returns the first path that exists, or null if none found.
57
+ * @param {string[]} candidatePaths - Relative paths to try (first found wins)
58
+ * @param {string} basePath - Service root directory
59
+ * @returns {string|null} Absolute path to the first existing file, or null
60
+ * @private
61
+ */
62
+ static _resolveConfigPath(candidatePaths, basePath) {
63
+ for (const relPath of candidatePaths) {
64
+ const abs = path.join(basePath, relPath);
65
+ if (fs.existsSync(abs)) {
66
+ return abs;
67
+ }
68
+ }
69
+ return null;
70
+ }
71
+
50
72
  /**
51
73
  * Ensure env is provided for placeholder resolution.
52
74
  * @param {any} env
@@ -194,21 +216,25 @@ class ConfigLoader {
194
216
  */
195
217
  static loadServiceConfig(options = {}) {
196
218
  const { basePath = process.cwd(), env } = options;
197
- const configPath = path.join(basePath, 'conn-config', 'config.json');
219
+ const reg = CONFIG_REGISTRY.SERVICE_CONFIG;
220
+ const configPath = this._resolveConfigPath(reg.paths, basePath);
221
+
222
+ if (!configPath) {
223
+ throw new Error(
224
+ `[ConfigLoader] Service config not found - tried: ${reg.paths.map(p => path.join(basePath, p)).join(', ')}`
225
+ );
226
+ }
198
227
 
199
228
  try {
200
229
  const content = fs.readFileSync(configPath, 'utf8');
201
230
  const config = JSON.parse(content);
202
231
 
203
- // Validate required fields (version not required - comes from package.json)
204
232
  if (!config.service || !config.service.name) {
205
233
  throw new Error('Invalid config.json: Missing required field (service.name)');
206
234
  }
207
235
 
208
- // Always use version from package.json (Single Source of Truth)
209
236
  config.service.version = this.loadPackageVersion(basePath);
210
237
 
211
- // Resolve env placeholders across the whole config (service + wrapper + custom sections)
212
238
  try {
213
239
  return this.resolveEnvPlaceholdersDeep(config, { jsonPath: '$', env });
214
240
  } catch (err) {
@@ -216,7 +242,7 @@ class ConfigLoader {
216
242
  }
217
243
  } catch (error) {
218
244
  if (error.code === 'ENOENT') {
219
- throw new Error(`Service config not found: ${configPath}`);
245
+ throw new Error(`[ConfigLoader] Service config not found: ${configPath}`);
220
246
  }
221
247
  throw error;
222
248
  }
@@ -229,13 +255,19 @@ class ConfigLoader {
229
255
  * @throws {Error} If operations file not found or invalid
230
256
  */
231
257
  static loadOperations(basePath = process.cwd()) {
232
- const operationsPath = path.join(basePath, 'conn-config', 'operations.json');
258
+ const reg = CONFIG_REGISTRY.OPERATIONS;
259
+ const operationsPath = this._resolveConfigPath(reg.paths, basePath);
260
+
261
+ if (!operationsPath) {
262
+ throw new Error(
263
+ `[ConfigLoader] Operations specification not found - tried: ${reg.paths.map(p => path.join(basePath, p)).join(', ')}`
264
+ );
265
+ }
233
266
 
234
267
  try {
235
268
  const content = fs.readFileSync(operationsPath, 'utf8');
236
269
  const operations = JSON.parse(content);
237
270
 
238
- // Validate structure
239
271
  if (!operations.operations || typeof operations.operations !== 'object') {
240
272
  throw new Error('Invalid operations.json: Missing or invalid "operations" field');
241
273
  }
@@ -243,7 +275,7 @@ class ConfigLoader {
243
275
  return operations;
244
276
  } catch (error) {
245
277
  if (error.code === 'ENOENT') {
246
- throw new Error(`Operations specification not found: ${operationsPath}`);
278
+ throw new Error(`[ConfigLoader] Operations specification not found: ${operationsPath}`);
247
279
  }
248
280
  throw error;
249
281
  }
@@ -255,18 +287,17 @@ class ConfigLoader {
255
287
  * @returns {Object|null} Validation proof or null if not found
256
288
  */
257
289
  static loadValidationProof(basePath = process.cwd()) {
258
- const proofPath = path.join(basePath, 'conn-runtime', 'validation-proof.json');
290
+ const reg = CONFIG_REGISTRY.VALIDATION_PROOF;
291
+ const proofPath = this._resolveConfigPath(reg.paths, basePath);
259
292
 
260
- try {
261
- // Check existence first (non-critical file)
262
- if (!fs.existsSync(proofPath)) {
263
- return null;
264
- }
293
+ if (!proofPath) {
294
+ return null;
295
+ }
265
296
 
297
+ try {
266
298
  const content = fs.readFileSync(proofPath, 'utf8');
267
299
  const proof = JSON.parse(content);
268
300
 
269
- // Validate structure
270
301
  if (!proof.validationProof && !proof.hash) {
271
302
  console.warn('[ConfigLoader] Invalid validation-proof.json structure - ignoring');
272
303
  return null;
@@ -277,7 +308,6 @@ class ConfigLoader {
277
308
  data: proof.validationData || proof.data
278
309
  };
279
310
  } catch (error) {
280
- // Non-critical - just log warning
281
311
  console.warn(`[ConfigLoader] Failed to load validation proof: ${error.message}`);
282
312
  return null;
283
313
  }
@@ -327,10 +357,9 @@ class ConfigLoader {
327
357
  customSections[k] = v;
328
358
  }
329
359
 
330
- // FAIL-FAST: specificationEndpoint is REQUIRED (no fallbacks)
331
360
  if (!serviceConfig.service.specificationEndpoint) {
332
361
  throw new Error(
333
- `[ConfigLoader] Missing required field - "specificationEndpoint" is required in conn-config/config.json under "service" section. ` +
362
+ `[ConfigLoader] Missing required field - "specificationEndpoint" is required in config/service/config.json (or conn-config/config.json) under "service" section. ` +
334
363
  `Fix: Add "specificationEndpoint": "/api/v1/specification" to config.json`
335
364
  );
336
365
  }
@@ -353,21 +382,29 @@ class ConfigLoader {
353
382
  }
354
383
 
355
384
  /**
356
- * Get path to config directory
385
+ * Get path to config directory (prefers config/service/, falls back to conn-config/)
357
386
  * @param {string} basePath - Base path (defaults to process.cwd())
358
- * @returns {string} Absolute path to conn-config directory
387
+ * @returns {string} Absolute path to config directory
359
388
  */
360
389
  static getConfigDir(basePath = process.cwd()) {
390
+ const newPath = path.join(basePath, 'config', 'service');
391
+ if (fs.existsSync(newPath)) {
392
+ return newPath;
393
+ }
361
394
  return path.join(basePath, 'conn-config');
362
395
  }
363
396
 
364
397
  /**
365
- * Get path to specific config file
398
+ * Get path to specific config file (prefers config/service/, falls back to conn-config/)
366
399
  * @param {string} filename - Config filename
367
400
  * @param {string} basePath - Base path (defaults to process.cwd())
368
401
  * @returns {string} Absolute path to config file
369
402
  */
370
403
  static getConfigPath(filename, basePath = process.cwd()) {
404
+ const newPath = path.join(basePath, 'config', 'service', filename);
405
+ if (fs.existsSync(newPath)) {
406
+ return newPath;
407
+ }
371
408
  return path.join(basePath, 'conn-config', filename);
372
409
  }
373
410
  }