@onlineapps/conn-orch-validator 3.3.1 → 4.0.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/CHANGELOG.md +389 -0
- package/README.md +46 -6
- package/TESTING_STRATEGY.md +1 -1
- package/docs/DESIGN.md +18 -11
- package/package.json +2 -3
- package/src/CookbookTestRunner.js +155 -26
- package/src/CookbookTestUtils.js +12 -4
- package/src/ServiceReadinessValidator.js +35 -18
- package/src/ValidationOrchestrator.js +223 -126
- package/src/cli/biz-ci-gate.js +356 -7
- package/src/helpers/README.md +3 -55
- package/src/helpers/createServiceReadinessTests.js +5 -1
- package/src/index.js +5 -2
- package/src/mocks/MockMQClient.js +100 -15
- package/src/utils/bizCiGateContract.js +110 -21
- package/src/utils/connectorContract.js +96 -0
- package/src/utils/cookbookFormat.js +95 -0
- package/src/utils/deployContract.js +488 -0
- package/src/utils/envContract.js +417 -0
- package/src/utils/installContract.js +142 -0
- package/src/utils/integrationRun.js +297 -0
- package/src/utils/libCompat.js +158 -0
- package/src/utils/preValidation.js +137 -0
- package/src/utils/setupDatabase.js +154 -0
- package/src/utils/stepFailure.js +73 -0
- package/src/utils/testNamespace.js +104 -0
- package/src/validators/ServiceStructureValidator.js +252 -45
- package/src/config.js +0 -32
- package/src/defaults.js +0 -11
- package/src/helpers/createPreValidationTests.js +0 -326
- package/test-mq-flow.js +0 -72
- package/test-orchestrator.js +0 -95
|
@@ -37,6 +37,184 @@ function hasConfigDir(root) {
|
|
|
37
37
|
fs.existsSync(path.join(root, 'conn-config'));
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// v1.2 — business-error CONTRACT (DÁVKA 50)
|
|
43
|
+
//
|
|
44
|
+
// Owner decision: api/docs/governance/confirmations/business-error-contract.md
|
|
45
|
+
// (20260829-1500-business-error-contract-001, CONFIRMED).
|
|
46
|
+
//
|
|
47
|
+
// v1.2 used to ask "does the service depend on @onlineapps/service-common and
|
|
48
|
+
// import an error class FROM IT?". Both retired checks (`dep_service_common`,
|
|
49
|
+
// `business_error_usage`) demanded the defeated hierarchy, so a service that had
|
|
50
|
+
// already migrated to the wrapper's classes failed the gate for being correct.
|
|
51
|
+
//
|
|
52
|
+
// The question is now the contract, which survives the package moving: does the
|
|
53
|
+
// service raise errors that the wrapper's ErrorMapper will translate — i.e. a
|
|
54
|
+
// BusinessError family from @onlineapps/service-wrapper, or its own class
|
|
55
|
+
// declaring the brand `onlineapps.businessError`?
|
|
56
|
+
//
|
|
57
|
+
// Scope is ANY file under src/, not "a handler or a src/lib/ module a handler
|
|
58
|
+
// requires" — one rule, one sentence (automation-gates.md §1 requirement 2,
|
|
59
|
+
// Simple). A static check cannot prove a `throw` reaches a handler anyway; it
|
|
60
|
+
// can only see the declaration. The narrower rule was measured on 2026-08-29
|
|
61
|
+
// and failed biz-converter and biz-ingest, both of which declare the contract in
|
|
62
|
+
// src/lib/ and reach it through src/services/. Lead decision, same day.
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
/** Names exported by the wrapper's error module that satisfy the contract. */
|
|
66
|
+
const WRAPPER_ERROR_NAMES = [
|
|
67
|
+
'BusinessError',
|
|
68
|
+
'ValidationError',
|
|
69
|
+
'NotFoundError',
|
|
70
|
+
'ConflictError',
|
|
71
|
+
'BusinessRuleError',
|
|
72
|
+
'AuthorizationError',
|
|
73
|
+
'InvalidEnvelopeError',
|
|
74
|
+
'BUSINESS_ERROR_BRAND'
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The RETIRED hierarchy: everything @onlineapps/service-common exports from
|
|
79
|
+
* src/errors/BusinessError.js. Deliberately NOT every error class in that
|
|
80
|
+
* package — `ScopedRegistryError` belongs to the live scoped-registry helper and
|
|
81
|
+
* is not part of what DÁVKA 50 retires.
|
|
82
|
+
*/
|
|
83
|
+
const RETIRED_ERROR_NAMES = [
|
|
84
|
+
'BusinessError',
|
|
85
|
+
'NotFoundError',
|
|
86
|
+
'ValidationError',
|
|
87
|
+
'ConflictError',
|
|
88
|
+
'BusinessRuleError',
|
|
89
|
+
'AuthorizationError',
|
|
90
|
+
'ServiceUnavailableError',
|
|
91
|
+
'isBusinessError',
|
|
92
|
+
'ERROR_TYPES',
|
|
93
|
+
'businessErrorHandler'
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
const BUSINESS_ERROR_BRAND_TOKEN = 'onlineapps.businessError';
|
|
97
|
+
|
|
98
|
+
function requirePattern(pkg) {
|
|
99
|
+
return new RegExp(`require\\(\\s*['"]${pkg.replace('/', '\\/')}['"]\\s*\\)`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Collect every .js file under `dir`, as paths relative to `root`. */
|
|
103
|
+
function listJsFiles(root, dir) {
|
|
104
|
+
const out = [];
|
|
105
|
+
if (!fs.existsSync(dir)) return out;
|
|
106
|
+
const walk = (d) => {
|
|
107
|
+
for (const entry of fs.readdirSync(d, { withFileTypes: true })) {
|
|
108
|
+
const p = path.join(d, entry.name);
|
|
109
|
+
if (entry.isDirectory()) walk(p);
|
|
110
|
+
else if (entry.isFile() && entry.name.endsWith('.js')) out.push(path.relative(root, p));
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
try { walk(dir); } catch { /* unreadable tree — callers treat as empty */ }
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function readFile(root, rel) {
|
|
118
|
+
try { return fs.readFileSync(path.join(root, rel), 'utf-8'); } catch { return null; }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Names destructured from `require('<pkg>')` in `content`, across every such
|
|
123
|
+
* statement (single-line or multi-line).
|
|
124
|
+
*
|
|
125
|
+
* The body is `[^{}]*?`, NOT `[\s\S]*?`: a lazy any-character body starts at the
|
|
126
|
+
* nearest preceding `const {` and swallows whole lines to reach the right
|
|
127
|
+
* `require`, which dropped the first imported name. Measured on
|
|
128
|
+
* `api_biz/meta/src/handlers/persons.js:10-11`, where an unrelated `models`
|
|
129
|
+
* destructuring sits directly above the service-common one.
|
|
130
|
+
*
|
|
131
|
+
* @returns {string[]}
|
|
132
|
+
*/
|
|
133
|
+
function destructuredFrom(content, pkg) {
|
|
134
|
+
const re = new RegExp(
|
|
135
|
+
`(?:const|let|var)\\s*\\{([^{}]*?)\\}\\s*=\\s*require\\(\\s*['"]${pkg.replace('/', '\\/')}['"]\\s*\\)`,
|
|
136
|
+
'g'
|
|
137
|
+
);
|
|
138
|
+
const names = [];
|
|
139
|
+
let m;
|
|
140
|
+
while ((m = re.exec(content)) !== null) {
|
|
141
|
+
for (const raw of m[1].split(',')) {
|
|
142
|
+
const name = raw.split(':')[0].trim();
|
|
143
|
+
if (name) names.push(name);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return names;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Does this file satisfy the business-error contract? */
|
|
150
|
+
function declaresBusinessErrorContract(content) {
|
|
151
|
+
if (content.includes(BUSINESS_ERROR_BRAND_TOKEN)) return true;
|
|
152
|
+
if (!requirePattern('@onlineapps/service-wrapper').test(content)) return false;
|
|
153
|
+
return destructuredFrom(content, '@onlineapps/service-wrapper')
|
|
154
|
+
.some(name => WRAPPER_ERROR_NAMES.includes(name));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* v1.2 checks: the business-error contract is declared somewhere under src/, and
|
|
159
|
+
* the retired hierarchy is gone from src/ entirely.
|
|
160
|
+
*
|
|
161
|
+
* @param {string} root - Service root
|
|
162
|
+
* @returns {Array<{ passed: boolean, id: string, message: string, fix?: string }>}
|
|
163
|
+
*/
|
|
164
|
+
function businessErrorChecks(root) {
|
|
165
|
+
const srcFiles = listJsFiles(root, path.join(root, 'src'));
|
|
166
|
+
|
|
167
|
+
let contractFile = null;
|
|
168
|
+
for (const rel of srcFiles) {
|
|
169
|
+
const content = readFile(root, rel);
|
|
170
|
+
if (content && declaresBusinessErrorContract(content)) { contractFile = rel; break; }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const contractCheck = contractFile
|
|
174
|
+
? {
|
|
175
|
+
passed: true,
|
|
176
|
+
id: 'business_error_contract',
|
|
177
|
+
message: `Business-error contract declared in ${contractFile}`
|
|
178
|
+
}
|
|
179
|
+
: {
|
|
180
|
+
passed: false,
|
|
181
|
+
id: 'business_error_contract',
|
|
182
|
+
message: '[ServiceStructureValidator] no business-error contract declared - ' +
|
|
183
|
+
'nothing under src/ imports a BusinessError family from @onlineapps/service-wrapper ' +
|
|
184
|
+
'or declares the brand ' + BUSINESS_ERROR_BRAND_TOKEN,
|
|
185
|
+
fix: 'Throw a contract-carrying error: ' +
|
|
186
|
+
"const { ValidationError } = require('@onlineapps/service-wrapper'); " +
|
|
187
|
+
`— or declare Symbol.for('${BUSINESS_ERROR_BRAND_TOKEN}') on the service's own error class.`
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const offenders = [];
|
|
191
|
+
for (const rel of listJsFiles(root, path.join(root, 'src'))) {
|
|
192
|
+
const content = readFile(root, rel);
|
|
193
|
+
if (!content) continue;
|
|
194
|
+
const names = destructuredFrom(content, '@onlineapps/service-common')
|
|
195
|
+
.filter(name => RETIRED_ERROR_NAMES.includes(name));
|
|
196
|
+
if (names.length > 0) offenders.push({ file: rel, names });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const retiredCheck = offenders.length === 0
|
|
200
|
+
? {
|
|
201
|
+
passed: true,
|
|
202
|
+
id: 'no_retired_error_hierarchy',
|
|
203
|
+
message: 'No @onlineapps/service-common error-hierarchy import under src/'
|
|
204
|
+
}
|
|
205
|
+
: {
|
|
206
|
+
passed: false,
|
|
207
|
+
id: 'no_retired_error_hierarchy',
|
|
208
|
+
message: '[ServiceStructureValidator] retired error hierarchy imported from @onlineapps/service-common - ' +
|
|
209
|
+
offenders.map(o => `${o.file} imports ${o.names.join(', ')}`).join('; '),
|
|
210
|
+
fix: offenders
|
|
211
|
+
.map(o => `Replace in ${o.file} with: const { ${o.names.join(', ')} } = require('@onlineapps/service-wrapper');`)
|
|
212
|
+
.join(' ')
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
return [contractCheck, retiredCheck];
|
|
216
|
+
}
|
|
217
|
+
|
|
40
218
|
const STANDARD_LEVELS = [
|
|
41
219
|
{
|
|
42
220
|
level: 'v1.0',
|
|
@@ -50,7 +228,9 @@ const STANDARD_LEVELS = [
|
|
|
50
228
|
results.push({ passed: hasConfigDir(root), id: 'dir_conn_config', message: 'config/service/ or conn-config/ directory' });
|
|
51
229
|
results.push({ passed: has('src'), id: 'dir_src', message: 'src/ directory' });
|
|
52
230
|
results.push({ passed: has('tests'), id: 'dir_tests', message: 'tests/ directory' });
|
|
53
|
-
|
|
231
|
+
// ADR 0005: src/handlers/ replaces src/app.js. Flat handlers/ or
|
|
232
|
+
// handlers/v3/ subdir are both valid canonical shapes.
|
|
233
|
+
results.push({ passed: has('src/handlers'), id: 'dir_handlers', message: 'src/handlers/ (v3 handler modules)' });
|
|
54
234
|
results.push({ passed: has('index.js'), id: 'file_index', message: 'index.js entry point' });
|
|
55
235
|
|
|
56
236
|
const configRaw = readConfigFile(root, 'config.json');
|
|
@@ -58,10 +238,11 @@ const STANDARD_LEVELS = [
|
|
|
58
238
|
if (configRaw) {
|
|
59
239
|
try {
|
|
60
240
|
const cfg = JSON.parse(configRaw);
|
|
61
|
-
|
|
241
|
+
// ADR 0005: service.port not required post zero-HTTP. Just name.
|
|
242
|
+
configValid = !!cfg.service?.name;
|
|
62
243
|
} catch { /* invalid JSON */ }
|
|
63
244
|
}
|
|
64
|
-
results.push({ passed: configValid, id: 'config_valid', message: 'Valid config.json with service.name
|
|
245
|
+
results.push({ passed: configValid, id: 'config_valid', message: 'Valid config.json with service.name' });
|
|
65
246
|
|
|
66
247
|
const opsRaw = readConfigFile(root, 'operations.json');
|
|
67
248
|
let opsValid = false;
|
|
@@ -108,24 +289,35 @@ const STANDARD_LEVELS = [
|
|
|
108
289
|
level: 'v1.2',
|
|
109
290
|
name: 'Business Error Handling Standard',
|
|
110
291
|
since: '2026-03-24',
|
|
292
|
+
checks: (root) => businessErrorChecks(root)
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
level: 'v1.3',
|
|
296
|
+
name: 'Zero-HTTP Shape (ADR 0005)',
|
|
297
|
+
since: '2026-08-17',
|
|
111
298
|
checks: (root) => {
|
|
299
|
+
const has = (p) => fs.existsSync(path.join(root, p));
|
|
112
300
|
const read = (p) => { try { return fs.readFileSync(path.join(root, p), 'utf-8'); } catch { return null; } };
|
|
113
301
|
|
|
302
|
+
// v1.3 asserts that F6/A2 cleanup landed: no dead Express surface.
|
|
303
|
+
const noAppJs = !has('src/app.js');
|
|
304
|
+
const noRoutesDir = !has('src/routes');
|
|
305
|
+
const noMiddlewaresDir = !has('src/middlewares');
|
|
306
|
+
|
|
114
307
|
const pkgRaw = read('package.json');
|
|
115
|
-
let
|
|
308
|
+
let noExpressDep = true;
|
|
116
309
|
if (pkgRaw) {
|
|
117
310
|
try {
|
|
118
311
|
const pkg = JSON.parse(pkgRaw);
|
|
119
|
-
|
|
120
|
-
} catch { /* invalid JSON */ }
|
|
312
|
+
noExpressDep = !pkg.dependencies?.express;
|
|
313
|
+
} catch { /* invalid JSON — separate check catches it */ }
|
|
121
314
|
}
|
|
122
315
|
|
|
123
|
-
const appContent = read('src/app.js') || '';
|
|
124
|
-
const hasErrorMiddleware = appContent.includes('businessErrorHandler');
|
|
125
|
-
|
|
126
316
|
return [
|
|
127
|
-
{ passed:
|
|
128
|
-
{ passed:
|
|
317
|
+
{ passed: noAppJs, id: 'no_app_js', message: 'src/app.js absent (retired Express dispatch surface)' },
|
|
318
|
+
{ passed: noRoutesDir, id: 'no_routes_dir', message: 'src/routes/ absent (dead HTTP route mounts)' },
|
|
319
|
+
{ passed: noMiddlewaresDir, id: 'no_middlewares_dir', message: 'src/middlewares/ absent (dead Express middleware)' },
|
|
320
|
+
{ passed: noExpressDep, id: 'no_express_dep', message: 'express not in package.json dependencies' }
|
|
129
321
|
];
|
|
130
322
|
}
|
|
131
323
|
}
|
|
@@ -218,12 +410,18 @@ class ServiceStructureValidator {
|
|
|
218
410
|
if (nextLevel) {
|
|
219
411
|
const failing = nextLevel.checks.filter(c => !c.passed);
|
|
220
412
|
for (const check of failing) {
|
|
413
|
+
// A check that carries its own `fix` has already written a
|
|
414
|
+
// `[Context] Problem - Expected/Fix` message naming the offending file;
|
|
415
|
+
// wrapping it in "missing …" would only bury the finding.
|
|
221
416
|
this.warnings.push({
|
|
222
417
|
type: 'STANDARD_LEVEL_GAP',
|
|
223
418
|
level: nextLevel.level,
|
|
224
419
|
check: check.id,
|
|
225
|
-
message:
|
|
226
|
-
|
|
420
|
+
message: check.fix
|
|
421
|
+
? `Standard ${nextLevel.level} (${nextLevel.name}): ${check.message}`
|
|
422
|
+
: `Standard ${nextLevel.level} (${nextLevel.name}): missing ${check.message}`,
|
|
423
|
+
fix: check.fix
|
|
424
|
+
|| `Implement ${check.message} to reach standard ${nextLevel.level}. See docs/biz/60-templates/service-template.md`
|
|
227
425
|
});
|
|
228
426
|
}
|
|
229
427
|
}
|
|
@@ -411,7 +609,8 @@ class ServiceStructureValidator {
|
|
|
411
609
|
|
|
412
610
|
/**
|
|
413
611
|
* Validate operations.json structure (v3 — handler registry dispatch).
|
|
414
|
-
*
|
|
612
|
+
*
|
|
613
|
+
* @see api/docs/biz/30-operations/schema-v3.md § File shape
|
|
415
614
|
*/
|
|
416
615
|
validateOperationsStructure(operations) {
|
|
417
616
|
if (!operations.operations) {
|
|
@@ -419,7 +618,7 @@ class ServiceStructureValidator {
|
|
|
419
618
|
type: 'INVALID_OPERATIONS_STRUCTURE',
|
|
420
619
|
path: 'config/service/operations.json',
|
|
421
620
|
message: 'operations.json must have "operations" key',
|
|
422
|
-
fix: 'Wrap operations in {"operations": {...}}
|
|
621
|
+
fix: 'Wrap operations in {"operations": {...}} in config/service/operations.json'
|
|
423
622
|
});
|
|
424
623
|
return;
|
|
425
624
|
}
|
|
@@ -442,7 +641,7 @@ class ServiceStructureValidator {
|
|
|
442
641
|
field: 'schema_version',
|
|
443
642
|
value: operations.schema_version,
|
|
444
643
|
message: `operations.json schema_version is "${operations.schema_version}" — expected "3.0"`,
|
|
445
|
-
fix: '
|
|
644
|
+
fix: 'Set schema_version to "3.0" in config/service/operations.json'
|
|
446
645
|
});
|
|
447
646
|
}
|
|
448
647
|
|
|
@@ -464,6 +663,8 @@ class ServiceStructureValidator {
|
|
|
464
663
|
/**
|
|
465
664
|
* Validate single operation structure (v3).
|
|
466
665
|
* Required: handler, bundle_scope. Forbidden (v2): endpoint, method, path.
|
|
666
|
+
*
|
|
667
|
+
* @see api/docs/biz/30-operations/schema-v3.md § Per-operation keys
|
|
467
668
|
*/
|
|
468
669
|
validateOperation(name, spec) {
|
|
469
670
|
const requiredFields = ['handler', 'bundle_scope'];
|
|
@@ -476,7 +677,7 @@ class ServiceStructureValidator {
|
|
|
476
677
|
operation: name,
|
|
477
678
|
field,
|
|
478
679
|
message: `Operation "${name}" missing required field: ${field}`,
|
|
479
|
-
fix: `Add "${field}" to operation "${name}"
|
|
680
|
+
fix: `Add "${field}" to operation "${name}" in config/service/operations.json`
|
|
480
681
|
});
|
|
481
682
|
}
|
|
482
683
|
}
|
|
@@ -517,7 +718,7 @@ class ServiceStructureValidator {
|
|
|
517
718
|
field: forbidden,
|
|
518
719
|
value: spec[forbidden],
|
|
519
720
|
message: `Operation "${name}" has retired v2 field "${forbidden}" — not allowed in v3 schema`,
|
|
520
|
-
fix: `Remove "${forbidden}" — v3 dispatches via handler registry
|
|
721
|
+
fix: `Remove "${forbidden}" from operation "${name}" in config/service/operations.json — v3 dispatches via the handler registry, not by URL`
|
|
521
722
|
});
|
|
522
723
|
}
|
|
523
724
|
}
|
|
@@ -594,39 +795,45 @@ class ServiceStructureValidator {
|
|
|
594
795
|
}
|
|
595
796
|
|
|
596
797
|
/**
|
|
597
|
-
* Validate source code structure
|
|
798
|
+
* Validate source code structure.
|
|
799
|
+
*
|
|
800
|
+
* ADR 0005 (2026-08-17): biz containers have zero HTTP surface —
|
|
801
|
+
* no Express, no `src/app.js`, no per-op HTTP routes. The v3
|
|
802
|
+
* handler-registry model instead requires `src/handlers/` (flat
|
|
803
|
+
* or `handlers/v3/` subdir — both variants of the canonical
|
|
804
|
+
* shape) with `handler` refs from `operations.json` resolvable
|
|
805
|
+
* to real exports.
|
|
598
806
|
*/
|
|
599
807
|
validateSourceStructure() {
|
|
600
|
-
|
|
601
|
-
|
|
808
|
+
// Post-ADR-0005 hard requirement: at least one handler module
|
|
809
|
+
// (flat or under v3/) must exist. HandlerRegistry.validate() at
|
|
810
|
+
// bootstrap time enforces per-op resolution; here we just ensure
|
|
811
|
+
// the directory itself is present so the service has something
|
|
812
|
+
// to dispatch to.
|
|
813
|
+
const handlersFlat = path.join(this.serviceRoot, 'src/handlers');
|
|
814
|
+
if (!fs.existsSync(handlersFlat)) {
|
|
602
815
|
this.errors.push({
|
|
603
|
-
type: '
|
|
604
|
-
path: 'src/
|
|
605
|
-
message: '
|
|
606
|
-
fix: 'Create src/
|
|
816
|
+
type: 'MISSING_HANDLERS',
|
|
817
|
+
path: 'src/handlers',
|
|
818
|
+
message: 'Handlers directory missing: src/handlers/',
|
|
819
|
+
fix: 'Create src/handlers/ with at least one v3 handler module. See: /docs/biz/60-templates/service-template.md'
|
|
607
820
|
});
|
|
608
821
|
} else {
|
|
609
|
-
this.info.push('✓ Found src/
|
|
822
|
+
this.info.push('✓ Found src/handlers/');
|
|
823
|
+
}
|
|
610
824
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
} catch (readError) {
|
|
624
|
-
this.warnings.push({
|
|
625
|
-
type: 'UNREADABLE_APP',
|
|
626
|
-
path: 'src/app.js',
|
|
627
|
-
message: `Could not read src/app.js: ${readError.message}`
|
|
628
|
-
});
|
|
629
|
-
}
|
|
825
|
+
// src/app.js is ANTI-PATTERN post-ADR 0005 (dead Express surface).
|
|
826
|
+
// Warn if present so authors of new services notice they should
|
|
827
|
+
// remove it; existing services scheduled to be cleaned up via the
|
|
828
|
+
// Fáze F6 pass.
|
|
829
|
+
const appPath = path.join(this.serviceRoot, 'src/app.js');
|
|
830
|
+
if (fs.existsSync(appPath)) {
|
|
831
|
+
this.warnings.push({
|
|
832
|
+
type: 'LEGACY_APP_JS',
|
|
833
|
+
path: 'src/app.js',
|
|
834
|
+
message: 'src/app.js is dead code post-ADR 0005 (bootstrap no longer reads it)',
|
|
835
|
+
fix: 'Delete src/app.js; move any residual middleware into the wrapper adapter. See: /docs/biz/80-decisions/0005-no-http-in-biz-containers.md'
|
|
836
|
+
});
|
|
630
837
|
}
|
|
631
838
|
|
|
632
839
|
const indexPath = path.join(this.serviceRoot, 'index.js');
|
package/src/config.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Runtime configuration schema for @onlineapps/conn-orch-validator.
|
|
5
|
-
*
|
|
6
|
-
* Uses @onlineapps/runtime-config for unified priority:
|
|
7
|
-
* 1. Explicit config (passed to ValidationOrchestrator/readiness options)
|
|
8
|
-
* 2. Environment variable
|
|
9
|
-
* 3. Module-owned defaults (none for topology)
|
|
10
|
-
*
|
|
11
|
-
* IMPORTANT: Integration test topology is FAIL-FAST (no defaults).
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const { createRuntimeConfig } = require('@onlineapps/runtime-config');
|
|
15
|
-
const DEFAULTS = require('./defaults');
|
|
16
|
-
|
|
17
|
-
const runtimeCfg = createRuntimeConfig({
|
|
18
|
-
defaults: DEFAULTS,
|
|
19
|
-
schema: {
|
|
20
|
-
serviceUrl: { env: 'TEST_SERVICE_URL', required: true },
|
|
21
|
-
mqUrl: { env: 'TEST_MQ_URL', required: true },
|
|
22
|
-
registryUrl: { env: 'TEST_REGISTRY_URL', required: true },
|
|
23
|
-
storageUrl: { env: 'TEST_STORAGE_URL', required: true },
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
module.exports = runtimeCfg;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|