@onlineapps/conn-orch-validator 7.0.0 → 8.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.
Files changed (102) hide show
  1. package/CHANGELOG.md +2558 -2
  2. package/README.md +1038 -4
  3. package/docs/DESIGN.md +3 -1
  4. package/manifests/biz-service.manifest.json +658 -0
  5. package/manifests/library.manifest.json +324 -0
  6. package/package.json +12 -6
  7. package/src/CookbookTestRunner.js +408 -101
  8. package/src/CookbookTestUtils.js +7 -8
  9. package/src/ServiceReadinessValidator.js +10 -35
  10. package/src/ValidationOrchestrator.js +219 -71
  11. package/src/cli/biz-ci-gate.js +176 -33
  12. package/src/cli/oa-lint-scripts.js +221 -0
  13. package/src/cli/oa-sync-template.js +1020 -0
  14. package/src/cli/oa-validate.js +474 -0
  15. package/src/helpers/README.md +2 -1
  16. package/src/helpers/createServiceReadinessTests.js +60 -4
  17. package/src/index.js +33 -3
  18. package/src/lint/scripts/lintScripts.js +298 -0
  19. package/src/manifest/checks/composeRunnerBlock.js +222 -0
  20. package/src/manifest/checks/composeShape.js +165 -0
  21. package/src/manifest/checks/contractBridge.js +181 -0
  22. package/src/manifest/checks/discoveryOrphan.js +50 -0
  23. package/src/manifest/checks/docsLintBridge.js +553 -0
  24. package/src/manifest/checks/fileAbsent.js +35 -0
  25. package/src/manifest/checks/gitTracked.js +204 -0
  26. package/src/manifest/checks/index.js +111 -0
  27. package/src/manifest/checks/libraryContext.js +226 -0
  28. package/src/manifest/checks/libraryDocs.js +75 -0
  29. package/src/manifest/checks/libraryPackage.js +272 -0
  30. package/src/manifest/checks/librarySource.js +274 -0
  31. package/src/manifest/checks/libraryTests.js +121 -0
  32. package/src/manifest/checks/libraryWorkspace.js +293 -0
  33. package/src/manifest/checks/readmeRegion.js +135 -0
  34. package/src/manifest/checks/scriptHeaders.js +79 -0
  35. package/src/manifest/checks/serviceConfig.js +390 -0
  36. package/src/manifest/checks/serviceConnectors.js +81 -0
  37. package/src/manifest/checks/serviceDb.js +388 -0
  38. package/src/manifest/checks/serviceFiles.js +754 -0
  39. package/src/manifest/checks/serviceIdentityRows.js +351 -0
  40. package/src/manifest/checks/serviceRuntime.js +295 -0
  41. package/src/manifest/checks/serviceScripts.js +213 -0
  42. package/src/manifest/deployabilitySignal.js +121 -0
  43. package/src/manifest/discovery.js +386 -0
  44. package/src/manifest/loadManifest.js +62 -0
  45. package/src/manifest/manifestShape.js +446 -0
  46. package/src/manifest/report.js +245 -0
  47. package/src/manifest/runManifest.js +449 -0
  48. package/src/manifest/serviceIdentity.js +140 -0
  49. package/src/manifest/walk.js +74 -0
  50. package/src/manifest/workspaceRoot.js +242 -0
  51. package/src/mocks/MockMQClient.js +13 -30
  52. package/src/mocks/MockRegistry.js +4 -2
  53. package/src/mocks/MockStorage.js +4 -2
  54. package/src/sync/docsRegion.js +463 -0
  55. package/src/sync/generatedRegion.js +228 -0
  56. package/src/sync/readmeLocation.js +182 -0
  57. package/src/sync/readmePointer.js +477 -0
  58. package/src/sync/serviceTemplate.js +583 -0
  59. package/src/sync/sharedEnv.js +162 -0
  60. package/src/sync/uniformFiles.js +474 -0
  61. package/src/utils/bizCiGateContract.js +131 -7
  62. package/src/utils/connectorContract.js +97 -7
  63. package/src/utils/cookbookFormat.js +81 -40
  64. package/src/utils/deployContract.js +140 -9
  65. package/src/utils/envContract.js +57 -1
  66. package/src/utils/handlerRef.js +181 -0
  67. package/src/utils/installContract.js +287 -41
  68. package/src/utils/libCompat.js +29 -7
  69. package/src/utils/migrationOrder.js +163 -0
  70. package/src/utils/preValidation.js +20 -7
  71. package/src/utils/setupDatabase.js +194 -13
  72. package/src/utils/testCoverageContract.js +539 -0
  73. package/src/utils/testNamespace.js +247 -23
  74. package/src/utils/throwawaySchema.js +207 -0
  75. package/src/validators/ServiceStructureValidator.js +2 -1
  76. package/templates/business-service/.dockerignore +42 -0
  77. package/templates/business-service/.gitlab-ci.yml +290 -0
  78. package/templates/business-service/Dockerfile +27 -0
  79. package/templates/business-service/README.md +213 -0
  80. package/templates/business-service/config/biz-docs-lint.tree.json +10 -0
  81. package/templates/business-service/config/env-templates/__SERVICE_NAME__.env +4 -0
  82. package/templates/business-service/config/env-templates/shared.env +65 -0
  83. package/templates/business-service/config/service/config.json +14 -0
  84. package/templates/business-service/config/service/integration-contract.json +12 -0
  85. package/templates/business-service/config/service/operations.json +41 -0
  86. package/templates/business-service/docker-compose.production.yml +60 -0
  87. package/templates/business-service/docker-compose.yml +93 -0
  88. package/templates/business-service/docs/80-setup/INSTALL.md +101 -0
  89. package/templates/business-service/docs/80-setup/PLATFORM_MATRIX.md +65 -0
  90. package/templates/business-service/docs/80-setup/README.md +18 -0
  91. package/templates/business-service/docs/80-setup/VALIDATION.md +78 -0
  92. package/templates/business-service/docs/README.md +18 -0
  93. package/templates/business-service/gitignore +42 -0
  94. package/templates/business-service/index.js +10 -0
  95. package/templates/business-service/init.sh +54 -0
  96. package/templates/business-service/jest.config.js +6 -0
  97. package/templates/business-service/package.json.template +31 -0
  98. package/templates/business-service/scripts/verify-deploy-uniform.sh +180 -0
  99. package/templates/business-service/src/handlers/v3/echo.js +39 -0
  100. package/templates/business-service/tests/cookbooks/echo.json +36 -0
  101. package/templates/business-service/tests/unit/handler.test.js +78 -0
  102. package/src/WorkflowTestRunner.js +0 -402
@@ -0,0 +1,181 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * The bridge from a manifest row to a checker this package ALREADY runs.
5
+ *
6
+ * Confirmation `biz-service-manifest` 004 point 4: a rule that exists is cited,
7
+ * never restated. So none of the checks below decides anything — each one calls
8
+ * the module that owns the rule, keeps the violations of the requirement its row
9
+ * names, and maps them into the two columns a finding adds (`where`, `what`).
10
+ * The row's `severity`, `owner`, `fix` and `doc` are the row's own, and
11
+ * `runManifest` puts them on the table.
12
+ *
13
+ * Three checkers, one shape (lead decision `api/shared/TODO.md` §0.2b-8 point 3):
14
+ *
15
+ * `deploy-contract` → utils/deployContract.js (R1, R4, … per row)
16
+ * `install-contract` → utils/installContract.js (the ids it exports)
17
+ * `env-contract` → utils/envContract.js (completeness; it has no requirement ids)
18
+ *
19
+ * Why a row must name its requirement rather than take the whole result: a
20
+ * checker raises several unrelated rules at once, and a table row is one rule
21
+ * with one owner and one fix. R2 (the deploy sequence) and R8 (tenant identity)
22
+ * are not rows of this uniform, and a row that swallowed them would report a
23
+ * finding no `fix` sentence of its own answers.
24
+ *
25
+ * @see api/docs/governance/confirmations/biz-service-manifest.md § Confirmation 20260909-biz-service-manifest-004
26
+ */
27
+
28
+ const path = require('path');
29
+
30
+ const { verifyDeployContract, DEPLOY_CONTRACT_REQUIREMENTS } = require('../../utils/deployContract');
31
+ const { verifyInstallContract, INSTALL_CONTRACT_REQUIREMENTS } = require('../../utils/installContract');
32
+ const { verifyEnvCompleteness } = require('../../utils/envContract');
33
+ const { loadAndValidateIntegrationContract } = require('../../utils/bizCiGateContract');
34
+
35
+ /** Where every service declares what it integrates with; three of these rows read it. */
36
+ const CONTRACT_PATH = 'config/service/integration-contract.json';
37
+
38
+ /**
39
+ * One violation as one table cell: the first line, without the `Fix:` sentence
40
+ * the row carries in its own column.
41
+ *
42
+ * @param {string} requirement the requirement id, kept so the table names it
43
+ * @param {string} message the checker's message, as it writes it
44
+ * @returns {string}
45
+ */
46
+ function summarise(requirement, message) {
47
+ const firstLine = String(message).split('\n')[0];
48
+ const withoutFix = firstLine.split(' Fix:')[0];
49
+ return `${requirement}: ${withoutFix.trim()}`;
50
+ }
51
+
52
+ /**
53
+ * Refuse a row whose requirement the checker does not raise. Silence would be
54
+ * indistinguishable from a clean repository, which is the failure mode this
55
+ * whole subject exists to end.
56
+ *
57
+ * @param {object} row the manifest row
58
+ * @param {ReadonlyArray<string>} known the ids the checker raises
59
+ * @param {string} module the module that owns them, for the message
60
+ */
61
+ function assertKnownRequirement(row, known, module) {
62
+ if (known.includes(row.requirement)) return;
63
+ throw new Error(`[ManifestBridge] Row ${row.id} names requirement "${row.requirement}", which `
64
+ + `${module} does not raise - Expected one of ${known.join(', ')}. `
65
+ + 'Fix: correct the row, or add the requirement to that module first; a row that cites a '
66
+ + 'requirement nobody raises reports nothing and reads as a pass.');
67
+ }
68
+
69
+ /**
70
+ * The service's normalized integration contract, or the reason it is unusable.
71
+ *
72
+ * A row that needs the contract and cannot read it says THAT — it never returns
73
+ * an empty list, which a reader would take for a clean repository. Row
74
+ * `C-CONTRACT` names the missing file with its own fix; this second sentence is
75
+ * about the requirement that could not be decided because of it.
76
+ *
77
+ * @param {string} serviceRoot repository root
78
+ * @returns {{ contract: object }|{ problem: string }}
79
+ */
80
+ function contractOf(serviceRoot) {
81
+ try {
82
+ return { contract: loadAndValidateIntegrationContract(serviceRoot).contract };
83
+ } catch (error) {
84
+ return { problem: String(error.message).split('\n')[0].trim() };
85
+ }
86
+ }
87
+
88
+ /**
89
+ * @param {object} row the manifest row
90
+ * @param {string} problem why the contract could not be read
91
+ * @returns {Array<{where: string, what: string}>}
92
+ */
93
+ const contractUnreadable = (row, problem) => [{
94
+ where: CONTRACT_PATH,
95
+ what: `${row.requirement} could not be decided — the integration contract is unreadable: ${problem}`
96
+ }];
97
+
98
+ const deployContract = Object.freeze({
99
+ scope: 'service',
100
+ requires: Object.freeze(['requirement', 'path']),
101
+
102
+ /**
103
+ * @param {{ row: object, serviceRoot: string }} params
104
+ * @returns {Array<{where: string, what: string}>}
105
+ */
106
+ run({ row, serviceRoot }) {
107
+ assertKnownRequirement(row, DEPLOY_CONTRACT_REQUIREMENTS, 'utils/deployContract.js');
108
+
109
+ return verifyDeployContract(serviceRoot).violations
110
+ .filter((violation) => violation.requirement === row.requirement)
111
+ .map((violation) => ({
112
+ where: row.path,
113
+ what: summarise(violation.requirement, violation.message)
114
+ }));
115
+ }
116
+ });
117
+
118
+ const installContract = Object.freeze({
119
+ scope: 'service',
120
+ requires: Object.freeze(['requirement', 'path']),
121
+
122
+ /**
123
+ * @param {{ row: object, serviceRoot: string }} params
124
+ * @returns {Array<{where: string, what: string}>}
125
+ */
126
+ run({ row, serviceRoot }) {
127
+ assertKnownRequirement(row, INSTALL_CONTRACT_REQUIREMENTS, 'utils/installContract.js');
128
+
129
+ const loaded = contractOf(serviceRoot);
130
+ if (loaded.problem !== undefined) return contractUnreadable(row, loaded.problem);
131
+
132
+ // Whether the SQL half applies at all is the contract's own decision, which
133
+ // is why the database block is handed over rather than derived here
134
+ // (installContract.js header: one fact, one owner).
135
+ return verifyInstallContract(serviceRoot, loaded.contract.database).violations
136
+ .filter((violation) => violation.requirement === row.requirement)
137
+ .map((violation) => ({
138
+ where: row.path,
139
+ what: summarise(violation.requirement, violation.message)
140
+ }));
141
+ }
142
+ });
143
+
144
+ const envContract = Object.freeze({
145
+ scope: 'service',
146
+ requires: Object.freeze([]),
147
+
148
+ /**
149
+ * Completeness (F16): every environment name the repository visibly reads is
150
+ * declared in the contract's `env` block or covered by the platform env. The
151
+ * rule, the scan and its blind spots are `utils/envContract.js`; this row
152
+ * places its violations on the table.
153
+ *
154
+ * @param {{ row: object, serviceRoot: string }} params
155
+ * @returns {Array<{where: string, what: string}>}
156
+ */
157
+ run({ row, serviceRoot }) {
158
+ const loaded = contractOf(serviceRoot);
159
+ if (loaded.problem !== undefined) {
160
+ return [{
161
+ where: CONTRACT_PATH,
162
+ what: `the environment contract could not be decided — the integration contract is unreadable: ${loaded.problem}`
163
+ }];
164
+ }
165
+
166
+ return verifyEnvCompleteness({ serviceRoot: path.resolve(serviceRoot), contract: loaded.contract }).violations
167
+ .map((violation) => ({
168
+ where: violation.sources[0],
169
+ what: `${violation.name} is read here, and the contract neither declares it `
170
+ + 'nor is it covered by the platform env'
171
+ }));
172
+ }
173
+ });
174
+
175
+ module.exports = {
176
+ checks: [
177
+ { name: 'deploy-contract', check: deployContract },
178
+ { name: 'install-contract', check: installContract },
179
+ { name: 'env-contract', check: envContract }
180
+ ]
181
+ };
@@ -0,0 +1,50 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * `discovery-orphan` — everything the discovery pattern's container holds is a
5
+ * bearer of some uniform, and its SSOT knows it.
6
+ *
7
+ * Completeness is discovery (confirmation biz-service-manifest 005 point 1): no
8
+ * declaration is read, because a bearer's uniform is its location. What the
9
+ * check compares is the disk against the file that owns the names — resolved at
10
+ * run time, never copied into the manifest (004 point 2).
11
+ *
12
+ * The check is workspace-scoped: inside a service container the SSOT is not
13
+ * present, and the row is then reported NOT RUN rather than passed. The same is
14
+ * true one level down, in a checkout that carries `api/` and not `api_biz/`: an
15
+ * absent container holds no directory, and "every directory is declared" would
16
+ * be true of nothing (`automation-gates.md` §5), so the row declares the
17
+ * container it walks and the runner stops it before it looks.
18
+ */
19
+
20
+ const { discoverBearers, rootOfPattern } = require('../discovery');
21
+
22
+ const check = Object.freeze({
23
+ scope: 'workspace',
24
+ requires: Object.freeze([]),
25
+
26
+ /** The directory the pattern walks; absent, the answer would be an empty disk. */
27
+ requiresSiblings({ block }) {
28
+ return [rootOfPattern(block.pattern)];
29
+ },
30
+
31
+ /** What the run prints instead of a verdict when the workspace is unreachable. */
32
+ describeNotRun({ block }) {
33
+ const owner = block && block.from ? block.from.path : 'the referenced SSOT';
34
+ return `the workspace root is not reachable, so ${owner} cannot be read`;
35
+ },
36
+
37
+ /**
38
+ * @param {{ block: object, workspaceRoot: string }} params
39
+ * @returns {Array<{where: string, what: string}>}
40
+ */
41
+ run({ block, workspaceRoot }) {
42
+ const { orphans } = discoverBearers({ block, workspaceRoot });
43
+ return orphans.map((orphan) => ({
44
+ where: orphan.relativeDir,
45
+ what: `wears no uniform — ${orphan.reason}`
46
+ }));
47
+ }
48
+ });
49
+
50
+ module.exports = { name: 'discovery-orphan', check };