@onlineapps/conn-orch-validator 3.3.1 → 3.3.2

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 CHANGED
@@ -2,6 +2,37 @@
2
2
 
3
3
  All notable changes to this package. Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format.
4
4
 
5
+ ## [3.3.2] — 2026-08-17
6
+
7
+ **ServiceStructureValidator refactor for ADR 0005 / Fáze F6 reality.**
8
+
9
+ 3.3.1 still required `src/app.js`, `service.port`, and `businessErrorHandler in src/app.js` — all dead after F6 removed Express. Any biz service whose validation-proof got invalidated post-F6 would hit `MISSING_APP` at Tier-1 step 1 and stall in restart loop. Surfaced when biz-meta's proof was manually invalidated in the Fáze F7 followup.
10
+
11
+ ### BREAKING
12
+
13
+ - **v1.0 Base Service Standard**: `src/app.js` no longer required. `src/handlers/` is now the required source dir (flat or `handlers/v3/` subdir — both are canonical variants). `service.port` no longer required in `config.json`.
14
+ - **v1.2 Business Error Handling**: `businessErrorHandler in src/app.js` check REMOVED (there is no src/app.js). Replaced with: at least one handler under `src/handlers/` imports and throws a `BusinessError` family class (`ValidationError`, `NotFoundError`, `ConflictError`, `ForbiddenError`) from `@onlineapps/service-common`. Errors flow through the wrapper's `ErrorMapper` post-A2.2, not through Express middleware.
15
+ - **`validateSourceStructure()`**: `MISSING_APP` error replaced with `MISSING_HANDLERS`. Presence of `src/app.js` now emits a **warning** (`LEGACY_APP_JS`) — dead code that F6 removes.
16
+
17
+ ### Added
18
+
19
+ - **v1.3 Zero-HTTP Shape (ADR 0005)** — new standard level asserting F6 cleanup landed:
20
+ - `src/app.js` absent
21
+ - `src/routes/` absent
22
+ - `src/middlewares/` absent
23
+ - `express` NOT in `package.json` dependencies
24
+
25
+ ### Rationale
26
+
27
+ Every biz service on the platform is post-F6 (verified in Fáze F acceptance ledger). Continuing to require Express artefacts would false-positive-fail every valid v3-canonical service.
28
+
29
+ ### Tests
30
+
31
+ Unit suite 158/158 GREEN before publish. Note: standard-level tests
32
+ (`StandardLevels.test.js`) were rewritten in the same commit to match
33
+ the post-F6 rules — 6 pre-existing test cases replaced with 6 matching
34
+ the new spec.
35
+
5
36
  ## [3.3.1] — 2026-08-17
6
37
 
7
38
  **Tier-1 step 5 refactor: retire HTTP `/health` probe per ADR 0005.**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/conn-orch-validator",
3
- "version": "3.3.1",
3
+ "version": "3.3.2",
4
4
  "description": "Validation orchestrator for OA Drive microservices - coordinates validation across all layers (base, infra, orch, business)",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -50,7 +50,9 @@ const STANDARD_LEVELS = [
50
50
  results.push({ passed: hasConfigDir(root), id: 'dir_conn_config', message: 'config/service/ or conn-config/ directory' });
51
51
  results.push({ passed: has('src'), id: 'dir_src', message: 'src/ directory' });
52
52
  results.push({ passed: has('tests'), id: 'dir_tests', message: 'tests/ directory' });
53
- results.push({ passed: has('src/app.js'), id: 'file_app', message: 'src/app.js' });
53
+ // ADR 0005: src/handlers/ replaces src/app.js. Flat handlers/ or
54
+ // handlers/v3/ subdir are both valid canonical shapes.
55
+ results.push({ passed: has('src/handlers'), id: 'dir_handlers', message: 'src/handlers/ (v3 handler modules)' });
54
56
  results.push({ passed: has('index.js'), id: 'file_index', message: 'index.js entry point' });
55
57
 
56
58
  const configRaw = readConfigFile(root, 'config.json');
@@ -58,10 +60,11 @@ const STANDARD_LEVELS = [
58
60
  if (configRaw) {
59
61
  try {
60
62
  const cfg = JSON.parse(configRaw);
61
- configValid = !!(cfg.service?.name && cfg.service?.port);
63
+ // ADR 0005: service.port not required post zero-HTTP. Just name.
64
+ configValid = !!cfg.service?.name;
62
65
  } catch { /* invalid JSON */ }
63
66
  }
64
- results.push({ passed: configValid, id: 'config_valid', message: 'Valid config.json with service.name + service.port' });
67
+ results.push({ passed: configValid, id: 'config_valid', message: 'Valid config.json with service.name' });
65
68
 
66
69
  const opsRaw = readConfigFile(root, 'operations.json');
67
70
  let opsValid = false;
@@ -120,12 +123,63 @@ const STANDARD_LEVELS = [
120
123
  } catch { /* invalid JSON */ }
121
124
  }
122
125
 
123
- const appContent = read('src/app.js') || '';
124
- const hasErrorMiddleware = appContent.includes('businessErrorHandler');
126
+ // ADR 0005 / F6: businessErrorHandler was Express middleware in
127
+ // src/app.js. Post-F6 there is no src/app.js. Error handling now
128
+ // happens via BusinessError thrown from handlers → wrapper's
129
+ // ErrorMapper. Check: at least one handler imports a
130
+ // BusinessError-family class from @onlineapps/service-common.
131
+ const handlersDir = path.join(root, 'src', 'handlers');
132
+ let hasBusinessErrorUsage = false;
133
+ if (fs.existsSync(handlersDir)) {
134
+ const walk = (dir) => {
135
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
136
+ const p = path.join(dir, entry.name);
137
+ if (entry.isDirectory()) walk(p);
138
+ else if (entry.isFile() && entry.name.endsWith('.js')) {
139
+ const content = read(path.relative(root, p));
140
+ if (content && /@onlineapps\/service-common/.test(content) &&
141
+ /BusinessError|ValidationError|NotFoundError|ConflictError|ForbiddenError/.test(content)) {
142
+ hasBusinessErrorUsage = true;
143
+ }
144
+ }
145
+ }
146
+ };
147
+ try { walk(handlersDir); } catch { /* ignore */ }
148
+ }
125
149
 
126
150
  return [
127
151
  { passed: hasServiceCommon, id: 'dep_service_common', message: '@onlineapps/service-common dependency' },
128
- { passed: hasErrorMiddleware, id: 'error_middleware', message: 'businessErrorHandler middleware in src/app.js' }
152
+ { passed: hasBusinessErrorUsage, id: 'business_error_usage', message: 'At least one handler throws a BusinessError family from @onlineapps/service-common' }
153
+ ];
154
+ }
155
+ },
156
+ {
157
+ level: 'v1.3',
158
+ name: 'Zero-HTTP Shape (ADR 0005)',
159
+ since: '2026-08-17',
160
+ checks: (root) => {
161
+ const has = (p) => fs.existsSync(path.join(root, p));
162
+ const read = (p) => { try { return fs.readFileSync(path.join(root, p), 'utf-8'); } catch { return null; } };
163
+
164
+ // v1.3 asserts that F6/A2 cleanup landed: no dead Express surface.
165
+ const noAppJs = !has('src/app.js');
166
+ const noRoutesDir = !has('src/routes');
167
+ const noMiddlewaresDir = !has('src/middlewares');
168
+
169
+ const pkgRaw = read('package.json');
170
+ let noExpressDep = true;
171
+ if (pkgRaw) {
172
+ try {
173
+ const pkg = JSON.parse(pkgRaw);
174
+ noExpressDep = !pkg.dependencies?.express;
175
+ } catch { /* invalid JSON — separate check catches it */ }
176
+ }
177
+
178
+ return [
179
+ { passed: noAppJs, id: 'no_app_js', message: 'src/app.js absent (retired Express dispatch surface)' },
180
+ { passed: noRoutesDir, id: 'no_routes_dir', message: 'src/routes/ absent (dead HTTP route mounts)' },
181
+ { passed: noMiddlewaresDir, id: 'no_middlewares_dir', message: 'src/middlewares/ absent (dead Express middleware)' },
182
+ { passed: noExpressDep, id: 'no_express_dep', message: 'express not in package.json dependencies' }
129
183
  ];
130
184
  }
131
185
  }
@@ -594,39 +648,45 @@ class ServiceStructureValidator {
594
648
  }
595
649
 
596
650
  /**
597
- * Validate source code structure
651
+ * Validate source code structure.
652
+ *
653
+ * ADR 0005 (2026-08-17): biz containers have zero HTTP surface —
654
+ * no Express, no `src/app.js`, no per-op HTTP routes. The v3
655
+ * handler-registry model instead requires `src/handlers/` (flat
656
+ * or `handlers/v3/` subdir — both variants of the canonical
657
+ * shape) with `handler` refs from `operations.json` resolvable
658
+ * to real exports.
598
659
  */
599
660
  validateSourceStructure() {
600
- const appPath = path.join(this.serviceRoot, 'src/app.js');
601
- if (!fs.existsSync(appPath)) {
661
+ // Post-ADR-0005 hard requirement: at least one handler module
662
+ // (flat or under v3/) must exist. HandlerRegistry.validate() at
663
+ // bootstrap time enforces per-op resolution; here we just ensure
664
+ // the directory itself is present so the service has something
665
+ // to dispatch to.
666
+ const handlersFlat = path.join(this.serviceRoot, 'src/handlers');
667
+ if (!fs.existsSync(handlersFlat)) {
602
668
  this.errors.push({
603
- type: 'MISSING_APP',
604
- path: 'src/app.js',
605
- message: 'Express application missing: src/app.js',
606
- fix: 'Create src/app.js with Express app. See: /docs/biz/60-templates/service-template.md'
669
+ type: 'MISSING_HANDLERS',
670
+ path: 'src/handlers',
671
+ message: 'Handlers directory missing: src/handlers/',
672
+ fix: 'Create src/handlers/ with at least one v3 handler module. See: /docs/biz/60-templates/service-template.md'
607
673
  });
608
674
  } else {
609
- this.info.push('✓ Found src/app.js');
675
+ this.info.push('✓ Found src/handlers/');
676
+ }
610
677
 
611
- try {
612
- const appContent = fs.readFileSync(appPath, 'utf-8');
613
- if (appContent.includes('businessErrorHandler')) {
614
- this.info.push('✓ businessErrorHandler middleware registered');
615
- } else {
616
- this.warnings.push({
617
- type: 'MISSING_ERROR_MIDDLEWARE',
618
- path: 'src/app.js',
619
- message: 'businessErrorHandler middleware not found in src/app.js',
620
- fix: 'Register businessErrorHandler from @onlineapps/service-common as Express error middleware. See: /docs/standards/ERROR_HANDLING.md'
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
- }
678
+ // src/app.js is ANTI-PATTERN post-ADR 0005 (dead Express surface).
679
+ // Warn if present so authors of new services notice they should
680
+ // remove it; existing services scheduled to be cleaned up via the
681
+ // Fáze F6 pass.
682
+ const appPath = path.join(this.serviceRoot, 'src/app.js');
683
+ if (fs.existsSync(appPath)) {
684
+ this.warnings.push({
685
+ type: 'LEGACY_APP_JS',
686
+ path: 'src/app.js',
687
+ message: 'src/app.js is dead code post-ADR 0005 (bootstrap no longer reads it)',
688
+ 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'
689
+ });
630
690
  }
631
691
 
632
692
  const indexPath = path.join(this.serviceRoot, 'index.js');