@topogram/cli 0.3.64 → 0.3.66

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 (278) hide show
  1. package/package.json +1 -1
  2. package/src/adoption/plan/index.js +716 -0
  3. package/src/adoption/plan.js +12 -703
  4. package/src/adoption/reporting.js +1 -1
  5. package/src/agent-brief.js +7 -21
  6. package/src/agent-ops/query-builders/auth.js +375 -0
  7. package/src/agent-ops/query-builders/change-risk/change-plan.js +123 -0
  8. package/src/agent-ops/query-builders/change-risk/import-plan.js +49 -0
  9. package/src/agent-ops/query-builders/change-risk/maintained.js +286 -0
  10. package/src/agent-ops/query-builders/change-risk/review-packets.js +123 -0
  11. package/src/agent-ops/query-builders/change-risk/risk.js +189 -0
  12. package/src/agent-ops/query-builders/change-risk.js +25 -0
  13. package/src/agent-ops/query-builders/common.js +149 -0
  14. package/src/agent-ops/query-builders/maintained-risk.js +539 -0
  15. package/src/agent-ops/query-builders/maintained-shared.js +120 -0
  16. package/src/agent-ops/query-builders/multi-agent.js +547 -0
  17. package/src/agent-ops/query-builders/projection-impacts.js +514 -0
  18. package/src/agent-ops/query-builders/work-packets.js +417 -0
  19. package/src/agent-ops/query-builders/workflow-context-shared.js +300 -0
  20. package/src/agent-ops/query-builders/workflow-context.js +398 -0
  21. package/src/agent-ops/query-builders/workflow-presets-core.js +677 -0
  22. package/src/agent-ops/query-builders/workflow-presets.js +341 -0
  23. package/src/agent-ops/query-builders.d.ts +26 -26
  24. package/src/agent-ops/query-builders.js +42 -5021
  25. package/src/archive/jsonl.js +2 -2
  26. package/src/archive/resolver-bridge.js +1 -1
  27. package/src/archive/unarchive.js +2 -1
  28. package/src/catalog/constants.js +10 -0
  29. package/src/catalog/copy.js +65 -0
  30. package/src/catalog/diagnostics.js +15 -0
  31. package/src/catalog/entries.js +42 -0
  32. package/src/catalog/files.js +67 -0
  33. package/src/catalog/provenance.js +123 -0
  34. package/src/catalog/source.js +150 -0
  35. package/src/catalog/validation.js +252 -0
  36. package/src/catalog.d.ts +2 -0
  37. package/src/catalog.js +18 -746
  38. package/src/cli/command-parsers/project.js +3 -0
  39. package/src/cli/command-parsers/shared.js +1 -1
  40. package/src/cli/commands/agent.js +2 -2
  41. package/src/cli/commands/catalog/check.js +31 -0
  42. package/src/cli/commands/catalog/copy.js +59 -0
  43. package/src/cli/commands/catalog/doctor.js +248 -0
  44. package/src/cli/commands/catalog/help.js +21 -0
  45. package/src/cli/commands/catalog/list.js +52 -0
  46. package/src/cli/commands/catalog/runner.js +92 -0
  47. package/src/cli/commands/catalog/shared.js +17 -0
  48. package/src/cli/commands/catalog/show.js +134 -0
  49. package/src/cli/commands/catalog.js +30 -615
  50. package/src/cli/commands/check.js +3 -3
  51. package/src/cli/commands/doctor.js +2 -9
  52. package/src/cli/commands/generator-policy/package-info.js +162 -0
  53. package/src/cli/commands/generator-policy/payloads.js +372 -0
  54. package/src/cli/commands/generator-policy/printers.js +159 -0
  55. package/src/cli/commands/generator-policy/runner.js +81 -0
  56. package/src/cli/commands/generator-policy/shared.js +39 -0
  57. package/src/cli/commands/generator-policy.js +15 -783
  58. package/src/cli/commands/import/adopt.js +170 -0
  59. package/src/cli/commands/import/check.js +91 -0
  60. package/src/cli/commands/import/diff.js +84 -0
  61. package/src/cli/commands/import/help.js +47 -0
  62. package/src/cli/commands/import/paths.js +269 -0
  63. package/src/cli/commands/import/plan.js +292 -0
  64. package/src/cli/commands/import/refresh.js +471 -0
  65. package/src/cli/commands/import/status-history.js +196 -0
  66. package/src/cli/commands/import/workspace.js +233 -0
  67. package/src/cli/commands/import.js +33 -1732
  68. package/src/cli/commands/migrate.js +153 -0
  69. package/src/cli/commands/package/constants.js +17 -0
  70. package/src/cli/commands/package/doctor.js +240 -0
  71. package/src/cli/commands/package/help.js +27 -0
  72. package/src/cli/commands/package/lockfile.js +135 -0
  73. package/src/cli/commands/package/npm.js +97 -0
  74. package/src/cli/commands/package/reporting.js +35 -0
  75. package/src/cli/commands/package/runner.js +33 -0
  76. package/src/cli/commands/package/shared.js +9 -0
  77. package/src/cli/commands/package/update-cli.js +252 -0
  78. package/src/cli/commands/package/versions.js +35 -0
  79. package/src/cli/commands/package.js +29 -813
  80. package/src/cli/commands/query/change-plan.js +68 -0
  81. package/src/cli/commands/query/definitions.js +202 -0
  82. package/src/cli/commands/query/import-adopt.js +121 -0
  83. package/src/cli/commands/query/runner/artifacts.js +102 -0
  84. package/src/cli/commands/query/runner/boundaries.js +211 -0
  85. package/src/cli/commands/query/runner/change.js +182 -0
  86. package/src/cli/commands/query/runner/import-adopt.js +111 -0
  87. package/src/cli/commands/query/runner/index.js +31 -0
  88. package/src/cli/commands/query/runner/output.js +12 -0
  89. package/src/cli/commands/query/runner/workflow.js +241 -0
  90. package/src/cli/commands/query/runner.js +3 -0
  91. package/src/cli/commands/query/workflow-context.js +5 -0
  92. package/src/cli/commands/query/workspace.js +270 -0
  93. package/src/cli/commands/query.js +9 -1300
  94. package/src/cli/commands/source.js +3 -12
  95. package/src/cli/commands/template/baseline.js +100 -0
  96. package/src/cli/commands/template/check.js +467 -0
  97. package/src/cli/commands/template/constants.js +8 -0
  98. package/src/cli/commands/template/diagnostics.js +26 -0
  99. package/src/cli/commands/template/help.js +28 -0
  100. package/src/cli/commands/template/lifecycle.js +404 -0
  101. package/src/cli/commands/template/list-show.js +287 -0
  102. package/src/cli/commands/template/policy.js +422 -0
  103. package/src/cli/commands/template/shared.js +127 -0
  104. package/src/cli/commands/template/updates.js +352 -0
  105. package/src/cli/commands/template-runner.js +6 -6
  106. package/src/cli/commands/template.js +41 -2143
  107. package/src/cli/commands/trust.js +1 -1
  108. package/src/cli/commands/workflow.js +6 -1
  109. package/src/cli/dispatcher.js +6 -1
  110. package/src/cli/help.js +15 -14
  111. package/src/cli/migration-guidance.js +1 -1
  112. package/src/cli/output-safety.js +2 -1
  113. package/src/cli/path-normalization.js +3 -13
  114. package/src/generator/api/contracts.js +497 -0
  115. package/src/generator/api/metadata.js +221 -0
  116. package/src/generator/api/openapi.js +559 -0
  117. package/src/generator/api/schema.js +124 -0
  118. package/src/generator/api/types.d.ts +98 -0
  119. package/src/generator/api.js +3 -1195
  120. package/src/generator/context/domain-page.js +1 -1
  121. package/src/generator/context/shared/domain-sdlc.js +282 -0
  122. package/src/generator/context/shared/maintained-boundary.js +665 -0
  123. package/src/generator/context/shared/metrics.js +85 -0
  124. package/src/generator/context/shared/primitives.js +64 -0
  125. package/src/generator/context/shared/relationships.js +453 -0
  126. package/src/generator/context/shared/summaries.js +263 -0
  127. package/src/generator/context/shared/types.d.ts +207 -0
  128. package/src/generator/context/shared.d.ts +42 -0
  129. package/src/generator/context/shared.js +80 -1390
  130. package/src/generator/context/slice/core.js +397 -0
  131. package/src/generator/context/slice/sdlc.js +417 -0
  132. package/src/generator/context/slice/ui-packets.js +183 -0
  133. package/src/generator/context/slice.js +2 -859
  134. package/src/generator/context/task-mode.js +2 -2
  135. package/src/generator/registry/index.js +507 -0
  136. package/src/generator/registry.js +18 -504
  137. package/src/generator/runtime/environment/index.js +666 -0
  138. package/src/generator/runtime/environment.js +4 -666
  139. package/src/generator/runtime/runtime-check/index.js +554 -0
  140. package/src/generator/runtime/runtime-check.js +4 -554
  141. package/src/generator/runtime/shared/index.js +572 -0
  142. package/src/generator/runtime/shared.js +19 -570
  143. package/src/generator/sdlc/doc-page.js +1 -1
  144. package/src/generator/shared.d.ts +2 -0
  145. package/src/generator/surfaces/databases/lifecycle-shared.js +1 -1
  146. package/src/generator/surfaces/native/swiftui-templates/README.generated.md +1 -1
  147. package/src/generator/surfaces/shared.d.ts +3 -0
  148. package/src/generator/widget-conformance/behavior-report.js +258 -0
  149. package/src/generator/widget-conformance/checks.js +371 -0
  150. package/src/generator/widget-conformance/projection-context.js +200 -0
  151. package/src/generator/widget-conformance/report.js +166 -0
  152. package/src/generator/widget-conformance/types.d.ts +121 -0
  153. package/src/generator/widget-conformance.js +3 -824
  154. package/src/import/core/context.d.ts +3 -0
  155. package/src/import/core/context.js +5 -7
  156. package/src/import/core/contracts.d.ts +1 -0
  157. package/src/import/core/registry.d.ts +4 -0
  158. package/src/import/core/runner/candidates.js +337 -0
  159. package/src/import/core/runner/options.js +22 -0
  160. package/src/import/core/runner/reports.js +51 -0
  161. package/src/import/core/runner/run.js +79 -0
  162. package/src/import/core/runner/tracks.js +150 -0
  163. package/src/import/core/runner/ui-drafts.js +393 -0
  164. package/src/import/core/runner.js +3 -698
  165. package/src/import/core/shared/api-routes.js +221 -0
  166. package/src/import/core/shared/candidates.js +97 -0
  167. package/src/import/core/shared/files.js +177 -0
  168. package/src/import/core/shared/next-app.js +389 -0
  169. package/src/import/core/shared/types.d.ts +51 -0
  170. package/src/import/core/shared/ui-routes.js +230 -0
  171. package/src/import/core/shared.js +60 -861
  172. package/src/new-project/constants.js +128 -0
  173. package/src/new-project/create.js +90 -0
  174. package/src/new-project/json.js +28 -0
  175. package/src/new-project/metadata.js +96 -0
  176. package/src/new-project/package-spec.js +161 -0
  177. package/src/new-project/project-files.js +351 -0
  178. package/src/new-project/template-policy.js +269 -0
  179. package/src/new-project/template-resolution.js +370 -0
  180. package/src/new-project/template-snapshots.js +442 -0
  181. package/src/new-project/template-updates.js +512 -0
  182. package/src/new-project/types.d.ts +83 -0
  183. package/src/new-project.js +6 -2277
  184. package/src/parser.d.ts +87 -1
  185. package/src/parser.js +118 -0
  186. package/src/policy/review-boundaries.d.ts +15 -0
  187. package/src/project-config/index.js +591 -0
  188. package/src/project-config.js +19 -561
  189. package/src/resolver/enrich/acceptance-criterion.js +2 -0
  190. package/src/resolver/enrich/bug.js +2 -0
  191. package/src/resolver/enrich/pitch.js +2 -0
  192. package/src/resolver/enrich/requirement.js +2 -0
  193. package/src/resolver/enrich/task.js +2 -0
  194. package/src/resolver/index.js +19 -2089
  195. package/src/resolver/normalize.js +384 -1
  196. package/src/resolver/plans.js +168 -0
  197. package/src/resolver/projections-api.js +494 -0
  198. package/src/resolver/projections-db.js +133 -0
  199. package/src/resolver/projections-ui.js +317 -0
  200. package/src/resolver/shapes.js +251 -0
  201. package/src/resolver/shared.js +278 -0
  202. package/src/resolver/widgets.js +132 -0
  203. package/src/sdlc/adopt.js +6 -5
  204. package/src/sdlc/paths.js +3 -5
  205. package/src/sdlc/scaffold.js +2 -1
  206. package/src/template-trust/constants.js +62 -0
  207. package/src/template-trust/content.js +258 -0
  208. package/src/template-trust/diff.js +92 -0
  209. package/src/template-trust/policy.js +61 -0
  210. package/src/template-trust/record.js +90 -0
  211. package/src/template-trust/status.js +182 -0
  212. package/src/template-trust.js +24 -687
  213. package/src/text-helpers.d.ts +1 -0
  214. package/src/topogram-types.d.ts +69 -0
  215. package/src/validator/common.js +488 -0
  216. package/src/validator/data-model.js +237 -0
  217. package/src/validator/docs.js +167 -0
  218. package/src/validator/expressions.js +146 -1
  219. package/src/validator/index.d.ts +23 -0
  220. package/src/validator/index.js +32 -3585
  221. package/src/validator/kinds.d.ts +41 -0
  222. package/src/validator/kinds.js +2 -0
  223. package/src/validator/model-helpers.js +46 -0
  224. package/src/validator/per-kind/acceptance-criterion.js +5 -0
  225. package/src/validator/per-kind/bug.js +6 -0
  226. package/src/validator/per-kind/domain.js +15 -2
  227. package/src/validator/per-kind/pitch.js +7 -0
  228. package/src/validator/per-kind/requirement.js +5 -0
  229. package/src/validator/per-kind/task.js +7 -0
  230. package/src/validator/per-kind/widget.js +14 -0
  231. package/src/validator/projections/api-http-async.js +410 -0
  232. package/src/validator/projections/api-http-authz.js +88 -0
  233. package/src/validator/projections/api-http-core.js +205 -0
  234. package/src/validator/projections/api-http-policies.js +339 -0
  235. package/src/validator/projections/api-http-responses.js +233 -0
  236. package/src/validator/projections/api-http.js +44 -0
  237. package/src/validator/projections/db.js +353 -0
  238. package/src/validator/projections/generator-defaults.js +45 -0
  239. package/src/validator/projections/helpers.js +87 -0
  240. package/src/validator/projections/ui-helpers.js +214 -0
  241. package/src/validator/projections/ui-navigation.js +344 -0
  242. package/src/validator/projections/ui-structure.js +364 -0
  243. package/src/validator/projections/ui-widgets.js +493 -0
  244. package/src/validator/projections/ui.js +46 -0
  245. package/src/validator/registry.js +48 -1
  246. package/src/validator/utils.d.ts +20 -0
  247. package/src/validator/utils.js +115 -12
  248. package/src/widget-behavior.d.ts +1 -0
  249. package/src/workflows/import-app/api/collect.js +221 -0
  250. package/src/workflows/import-app/api/openapi.js +257 -0
  251. package/src/workflows/import-app/api/routes.js +327 -0
  252. package/src/workflows/import-app/api/sources.js +22 -0
  253. package/src/workflows/import-app/api.js +2 -797
  254. package/src/workflows/reconcile/adoption-plan/build.js +212 -0
  255. package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
  256. package/src/workflows/reconcile/adoption-plan/outputs.js +153 -0
  257. package/src/workflows/reconcile/adoption-plan/paths.js +58 -0
  258. package/src/workflows/reconcile/adoption-plan/projection-patches.js +177 -0
  259. package/src/workflows/reconcile/adoption-plan/reasons.js +107 -0
  260. package/src/workflows/reconcile/adoption-plan.js +30 -740
  261. package/src/workflows/reconcile/auth/closures.js +115 -0
  262. package/src/workflows/reconcile/auth/formatters.js +142 -0
  263. package/src/workflows/reconcile/auth/inference.js +330 -0
  264. package/src/workflows/reconcile/auth/roles.js +122 -0
  265. package/src/workflows/reconcile/auth.js +35 -690
  266. package/src/workflows/reconcile/bundle-core/index.js +600 -0
  267. package/src/workflows/reconcile/bundle-core.js +12 -598
  268. package/src/workflows/reconcile/candidate-model.js +18 -2
  269. package/src/workflows/reconcile/canonical-surface.js +1 -1
  270. package/src/workflows/reconcile/impacts/adoption-plan.js +196 -0
  271. package/src/workflows/reconcile/impacts/indexes.js +105 -0
  272. package/src/workflows/reconcile/impacts/patches.js +252 -0
  273. package/src/workflows/reconcile/impacts/reports.js +80 -0
  274. package/src/workflows/reconcile/impacts.js +14 -623
  275. package/src/workflows/reconcile/renderers.js +41 -6
  276. package/src/workflows/shared.js +5 -11
  277. package/src/workspace-docs.d.ts +29 -0
  278. package/src/workspace-paths.js +328 -0
@@ -1,554 +1,4 @@
1
- import {
2
- generateRuntimeApiContracts,
3
- buildVerificationSummary,
4
- getDefaultEnvironmentProjections,
5
- resolveRuntimeTopology,
6
- runtimeDemoUserId,
7
- selectChecksByVerification,
8
- runtimePorts,
9
- runtimeUrls
10
- } from "./shared.js";
11
- import { getExampleImplementation } from "../../example-implementation.js";
12
- import { renderNodeScriptRunner } from "./bundle-shared.js";
13
-
14
- function buildRuntimeCheckPlan(graph, options = {}) {
15
- const implementation = getExampleImplementation(graph, options);
16
- const runtimeReference = implementation.runtime.reference;
17
- const runtimeChecks = implementation.runtime.checks;
18
- const topology = resolveRuntimeTopology(graph, options);
19
- const { apiProjection, uiProjection, dbProjection } = getDefaultEnvironmentProjections(graph, options);
20
- const verification = buildVerificationSummary(graph, ["runtime", "contract", "journey"]);
21
- const apiSelection = selectChecksByVerification(graph, runtimeChecks.apiStage.checks, ["runtime", "contract", "journey"], {
22
- keepLookupChecks: true
23
- });
24
- const resolveLookupCheck = (check) => {
25
- if (!check.lookupKey) {
26
- return check;
27
- }
28
- const { lookupKey, ...rest } = check;
29
- const path = runtimeReference.runtimeCheck.lookupPaths[check.lookupKey];
30
- return path ? { ...rest, path } : rest;
31
- };
32
- return {
33
- type: "runtime_check_plan",
34
- name: runtimeReference.runtimeCheck.name,
35
- projections: {
36
- api: apiProjection.id,
37
- ui: uiProjection.id,
38
- db: dbProjection.id
39
- },
40
- topology: {
41
- runtimes: topology.runtimes.map((runtime) => ({
42
- id: runtime.id,
43
- kind: runtime.kind,
44
- projection: runtime.projection.id,
45
- generator: runtime.generator
46
- }))
47
- },
48
- ...(verification ? { verification } : {}),
49
- ...(apiSelection.selection ? { selection: apiSelection.selection } : {}),
50
- env: {
51
- required: [
52
- ...runtimeReference.runtimeCheck.requiredEnv
53
- ],
54
- apiBase: "TOPOGRAM_API_BASE_URL",
55
- webBase: "TOPOGRAM_WEB_BASE_URL",
56
- demoContainerId: runtimeReference.runtimeCheck.demoContainerEnvVar,
57
- demoPrimaryId: runtimeReference.runtimeCheck.demoPrimaryEnvVar,
58
- demoUserId: "TOPOGRAM_DEMO_USER_ID"
59
- },
60
- report: {
61
- outputPath: "state/runtime-check-report.json"
62
- },
63
- stages: [
64
- {
65
- ...runtimeChecks.environmentStage,
66
- checks: runtimeChecks.environmentStage.checks.map(resolveLookupCheck)
67
- },
68
- {
69
- ...runtimeChecks.apiStage,
70
- checks: apiSelection.checks.map(resolveLookupCheck)
71
- }
72
- ]
73
- };
74
- }
75
-
76
- function renderRuntimeCheckEnvExample(graph, options = {}) {
77
- const runtimeReference = getExampleImplementation(graph, options).runtime.reference;
78
- const urls = runtimeUrls(runtimeReference, resolveRuntimeTopology(graph, options));
79
- return `TOPOGRAM_API_BASE_URL=${urls.api}
80
- TOPOGRAM_WEB_BASE_URL=${urls.web}
81
- TOPOGRAM_DEMO_USER_ID=${runtimeDemoUserId(runtimeReference)}
82
- ${runtimeReference.environment.envExample || ""}
83
- `;
84
- }
85
-
86
- function renderRuntimeCheckReadme(graph, options = {}) {
87
- const runtimeReference = getExampleImplementation(graph, options).runtime.reference;
88
- const verification = buildVerificationSummary(graph, ["runtime", "contract", "journey"]);
89
- const stageNames = (runtimeReference.runtimeCheck.stageNotes && runtimeReference.runtimeCheck.stageNotes.length > 0)
90
- ? runtimeReference.runtimeCheck.stageNotes
91
- : [
92
- {
93
- id: "environment",
94
- summary: "required env, web readiness, API health, API readiness, and seeded data checks"
95
- },
96
- {
97
- id: "api",
98
- summary: "core API happy paths, lookup endpoints, and important negative cases"
99
- }
100
- ];
101
- const notes = (runtimeReference.runtimeCheck.notes && runtimeReference.runtimeCheck.notes.length > 0)
102
- ? runtimeReference.runtimeCheck.notes
103
- : [
104
- "Mutating checks exercise the example's create/update/terminal flows.",
105
- "Lookup checks verify the generated lookup endpoints configured for the example.",
106
- "Later stages are skipped if environment readiness fails.",
107
- "The generated server exposes both `/health` and `/ready`.",
108
- "Use the smoke bundle for a faster minimal confidence check.",
109
- "Use this runtime-check bundle for richer staged verification and JSON reporting."
110
- ];
111
- return `# ${runtimeReference.runtimeCheck.bundleTitle}
112
-
113
- This bundle gives you richer staged runtime verification for the generated stack.
114
-
115
- Use it when you want more than a quick smoke test. It goes beyond the lightweight smoke bundle by checking environment readiness, API health, DB-backed seeded data, and deeper API behavior.
116
-
117
- ## Stages
118
-
119
- ${stageNames.map((stage) => `- \`${stage.id}\`: ${stage.summary}`).join("\n")}
120
-
121
- ## Usage
122
-
123
- 1. Copy \`.env.example\` to \`.env\` if needed
124
- 2. Run \`bash ./scripts/check.sh\`
125
- 3. Inspect \`state/runtime-check-report.json\`
126
-
127
- ## Notes
128
-
129
- ${notes.map((note) => `- ${note}`).join("\n")}
130
- ${verification ? `\n## Canonical Verification\n\n- Sources: ${verification.sources.map((entry) => `\`${entry.id}\``).join(", ")}\n- Scenarios: ${verification.scenarios.map((entry) => entry.label).join(", ")}` : ""}
131
- `;
132
- }
133
-
134
- function renderRuntimeCheckScript() {
135
- return renderNodeScriptRunner("check.mjs", { searchParentEnv: true });
136
- }
137
-
138
- function renderRuntimeCheckModule(graph, options = {}) {
139
- const implementation = getExampleImplementation(graph, options);
140
- const runtimeReference = implementation.runtime.reference;
141
- const runtimeCheckRenderers = implementation.runtime.checkRenderers;
142
- const ports = runtimePorts(runtimeReference, resolveRuntimeTopology(graph, options));
143
- return `import fs from "node:fs/promises";
144
- import { execFile } from "node:child_process";
145
- import path from "node:path";
146
- import { promisify } from "node:util";
147
- import { fileURLToPath } from "node:url";
148
-
149
- const __filename = fileURLToPath(import.meta.url);
150
- const __dirname = path.dirname(__filename);
151
- const rootDir = path.resolve(__dirname, "..");
152
- const plan = JSON.parse(await fs.readFile(path.join(rootDir, "runtime-check-plan.json"), "utf8"));
153
- const apiContracts = JSON.parse(await fs.readFile(path.join(rootDir, "api-contracts.json"), "utf8"));
154
- const reportPath = path.join(rootDir, plan.report.outputPath);
155
- const execFileAsync = promisify(execFile);
156
-
157
- const envVars = Object.fromEntries(Object.entries(plan.env).filter(([, value]) => typeof value === "string"));
158
- ${runtimeCheckRenderers.renderRuntimeCheckState()}
159
-
160
- function envValue(name) {
161
- const direct = process.env[name];
162
- if (direct) {
163
- return direct;
164
- }
165
-
166
- const fallbackMap = {
167
- TOPOGRAM_API_BASE_URL: process.env.PUBLIC_TOPOGRAM_API_BASE_URL || "http://localhost:${ports.server}",
168
- TOPOGRAM_WEB_BASE_URL: process.env.PUBLIC_TOPOGRAM_WEB_BASE_URL || \`http://localhost:\${process.env.WEB_PORT || "${ports.web}"}\`,
169
- ${runtimeReference.runtimeCheck.demoContainerEnvVar}: process.env.PUBLIC_TOPOGRAM_DEMO_CONTAINER_ID || "",
170
- ${runtimeReference.runtimeCheck.demoPrimaryEnvVar}: process.env.PUBLIC_TOPOGRAM_DEMO_PRIMARY_ID || "",
171
- TOPOGRAM_DEMO_USER_ID: process.env.PUBLIC_TOPOGRAM_AUTH_USER_ID || process.env.PUBLIC_TOPOGRAM_DEMO_USER_ID || "",
172
- TOPOGRAM_AUTH_USER_ID: process.env.TOPOGRAM_DEMO_USER_ID || ""
173
- };
174
-
175
- return fallbackMap[name] || "";
176
- }
177
-
178
- function apiBase() {
179
- return envValue(plan.env.apiBase);
180
- }
181
-
182
- function webBase() {
183
- return envValue(plan.env.webBase);
184
- }
185
-
186
- function stackStartHint() {
187
- return "Start the generated stack with 'npm run dev' from the app bundle, or 'npm run app:dev' from the project root, then rerun this command.";
188
- }
189
-
190
- function describeFetchError(error) {
191
- if (error?.cause?.code) {
192
- return error.cause.code;
193
- }
194
- if (Array.isArray(error?.cause?.errors) && error.cause.errors.length > 0) {
195
- return [...new Set(error.cause.errors.map((entry) => entry.code).filter(Boolean))].join(", ");
196
- }
197
- return error instanceof Error ? error.message : String(error);
198
- }
199
-
200
- async function fetchWithStackHint(url, init, label) {
201
- try {
202
- return await fetch(url, init);
203
- } catch (error) {
204
- throw new Error(\`\${label} is not reachable at \${url.toString()}. \${stackStartHint()} Original error: \${describeFetchError(error)}\`);
205
- }
206
- }
207
-
208
- function resolveCheckPath(pathTemplate) {
209
- return String(pathTemplate || "")
210
- .replace(/\\$env:([A-Z0-9_]+)/g, (_, name) => encodeURIComponent(envValue(name)))
211
- .replace(/\\$state:primary_id/g, encodeURIComponent(String(state.latestPrimary?.id || state.createdPrimary?.id || "")));
212
- }
213
-
214
- function authToken() {
215
- return envValue("TOPOGRAM_AUTH_TOKEN");
216
- }
217
-
218
- function pathParamValue(token) {
219
- if (typeof token !== "string") {
220
- return token;
221
- }
222
- if (token.startsWith("$env:")) {
223
- return envValue(token.slice(5));
224
- }
225
- if (token === "$state:primary_id") {
226
- return state.latestPrimary?.id || state.createdPrimary?.id || "";
227
- }
228
- return token;
229
- }
230
-
231
- function contractFor(capabilityId) {
232
- const contract = apiContracts[capabilityId];
233
- if (!contract) {
234
- throw new Error(\`Missing API contract for \${capabilityId}\`);
235
- }
236
- return contract;
237
- }
238
-
239
- function buildUrl(base, endpointPath, pathParams = {}) {
240
- let resolvedPath = endpointPath;
241
- for (const [name, rawValue] of Object.entries(pathParams)) {
242
- const value = pathParamValue(rawValue);
243
- resolvedPath = resolvedPath.replace(\`:\${name}\`, encodeURIComponent(String(value)));
244
- }
245
- return new URL(resolvedPath, base);
246
- }
247
-
248
- function inferPathParams(contract, overrides = {}) {
249
- const params = {};
250
- for (const field of contract.requestContract?.transport?.path || []) {
251
- if (Object.prototype.hasOwnProperty.call(overrides, field.name)) {
252
- params[field.transport.wireName] = overrides[field.name];
253
- continue;
254
- }
255
- if (
256
- field.name.endsWith("_id") &&
257
- field.transport?.wireName === "id" &&
258
- field.name !== "job_id"
259
- ) {
260
- params[field.transport.wireName] = "$state:primary_id";
261
- continue;
262
- }
263
- if (field.name === "job_id") {
264
- throw new Error("Runtime checks do not support job_id paths in v1");
265
- }
266
- }
267
- return params;
268
- }
269
-
270
- ${runtimeCheckRenderers.renderRuntimeCheckCreatePayload()}
271
-
272
- function assertCondition(condition, message) {
273
- if (!condition) {
274
- throw new Error(message);
275
- }
276
- }
277
-
278
- function assertErrorResponse(responseBody, expectedCode, label) {
279
- assertCondition(responseBody && typeof responseBody === "object", \`\${label} did not return a JSON object body\`);
280
- assertCondition(responseBody.error && typeof responseBody.error === "object", \`\${label} did not include an error object\`);
281
- assertCondition(typeof responseBody.error.code === "string" && responseBody.error.code.length > 0, \`\${label} did not include an error code\`);
282
- assertCondition(typeof responseBody.error.message === "string" && responseBody.error.message.length > 0, \`\${label} did not include an error message\`);
283
- if (expectedCode) {
284
- assertCondition(responseBody.error.code === expectedCode, \`\${label} expected error code \${expectedCode}, got \${responseBody.error.code}\`);
285
- }
286
- }
287
-
288
- function sleep(ms) {
289
- return new Promise((resolve) => setTimeout(resolve, ms));
290
- }
291
-
292
- function appleScriptString(value) {
293
- return JSON.stringify(String(value ?? ""));
294
- }
295
-
296
- async function runSafariBrowserCheck(definition) {
297
- if (process.platform !== "darwin") {
298
- throw new Error("web_browser_contract is only supported on macOS local stacks");
299
- }
300
-
301
- const targetUrl = new URL(resolveCheckPath(definition.path), webBase()).toString();
302
- const waitMs = Number(process.env.TOPOGRAM_BROWSER_WAIT_MS || "8000");
303
- const pollMs = Number(process.env.TOPOGRAM_BROWSER_POLL_MS || "250");
304
- const pollLimit = Math.max(1, Math.ceil(waitMs / pollMs));
305
- const pollDelaySeconds = pollMs / 1000;
306
- const browserApp = process.env.TOPOGRAM_BROWSER_APP || "Safari";
307
- const script = [
308
- \`set targetUrl to \${appleScriptString(targetUrl)}\`,
309
- \`set expectedText to \${appleScriptString(definition.expectText || "")}\`,
310
- \`set forbiddenText to \${appleScriptString(definition.expectNotText || "")}\`,
311
- \`set pollLimit to \${appleScriptString(pollLimit)} as integer\`,
312
- \`set pollDelay to \${appleScriptString(pollDelaySeconds)} as real\`,
313
- \`tell application \${appleScriptString(browserApp)}\`,
314
- "activate",
315
- "set runtimeDoc to make new document with properties {URL:targetUrl}",
316
- "repeat with attempt from 1 to pollLimit",
317
- "delay pollDelay",
318
- "try",
319
- "set bodyText to do JavaScript \\"document.body ? document.body.innerText : ''\\" in runtimeDoc",
320
- "if (expectedText is \\"\\" or bodyText contains expectedText) and (forbiddenText is \\"\\" or bodyText does not contain forbiddenText) then exit repeat",
321
- "end try",
322
- "end repeat",
323
- "set finalText to do JavaScript \\"document.body ? document.body.innerText : ''\\" in runtimeDoc",
324
- "close runtimeDoc",
325
- "return finalText",
326
- "end tell"
327
- ];
328
- const args = script.flatMap((line) => ["-e", line]);
329
- const { stdout, stderr } = await execFileAsync("osascript", args);
330
- if (stderr && stderr.trim()) {
331
- throw new Error(\`browser check failed: \${stderr.trim()}\`);
332
- }
333
- return stdout.trim();
334
- }
335
-
336
- ${runtimeCheckRenderers.renderRuntimeCheckHelpers()}
337
-
338
- async function parseResponseBody(response) {
339
- const text = await response.text();
340
- if (!text) {
341
- return null;
342
- }
343
- const contentType = response.headers.get("content-type") || "";
344
- if (contentType.includes("application/json")) {
345
- return JSON.parse(text);
346
- }
347
- return text;
348
- }
349
-
350
- async function requestContract(capabilityId, { payload = null, pathParams = null, headers = {} } = {}) {
351
- const contract = contractFor(capabilityId);
352
- const resolvedPathParams = pathParams || inferPathParams(contract);
353
- const url = buildUrl(apiBase(), contract.endpoint.path, resolvedPathParams);
354
- const requestHeaders = new Headers(headers);
355
- if ((contract.endpoint.authz || []).length > 0 && authToken() && !requestHeaders.has("Authorization")) {
356
- requestHeaders.set("Authorization", \`Bearer \${authToken()}\`);
357
- }
358
- let body;
359
- if (payload && (contract.requestContract?.transport?.body || []).length > 0) {
360
- requestHeaders.set("content-type", "application/json");
361
- body = JSON.stringify(payload);
362
- }
363
- const response = await fetchWithStackHint(url, {
364
- method: contract.endpoint.method,
365
- headers: requestHeaders,
366
- body
367
- }, "api service");
368
- const responseBody = await parseResponseBody(response);
369
- return { contract, response, responseBody, url: url.toString() };
370
- }
371
-
372
- function preconditionStatusFor(contract) {
373
- return contract.endpoint.preconditions?.[0]?.error || 412;
374
- }
375
-
376
- function visibleTextFromHtml(html) {
377
- return String(html || "")
378
- .replace(/<script\\b[^>]*>[\\s\\S]*?<\\/script>/gi, " ")
379
- .replace(/<style\\b[^>]*>[\\s\\S]*?<\\/style>/gi, " ")
380
- .replace(/<[^>]+>/g, " ")
381
- .replace(/&nbsp;/g, " ")
382
- .replace(/&amp;/g, "&")
383
- .replace(/&lt;/g, "<")
384
- .replace(/&gt;/g, ">")
385
- .replace(/&quot;/g, '"')
386
- .replace(/&#39;/g, "'")
387
- .replace(/\\s+/g, " ")
388
- .trim();
389
- }
390
-
391
- function checkResultTemplate(definition) {
392
- return {
393
- id: definition.id,
394
- kind: definition.kind,
395
- mandatory: definition.mandatory !== false,
396
- mutating: Boolean(definition.mutating),
397
- ok: null,
398
- skipped: false,
399
- durationMs: 0,
400
- error: null,
401
- resources: {}
402
- };
403
- }
404
-
405
- async function runCheck(definition) {
406
- const startedAt = Date.now();
407
- const result = checkResultTemplate(definition);
408
- try {
409
- if (definition.id === "required_env") {
410
- const missing = plan.env.required.filter((name) => !envValue(name));
411
- assertCondition(missing.length === 0, \`Missing required environment variables: \${missing.join(", ")}\`);
412
- } else if (definition.kind === "web_contract") {
413
- const response = await fetchWithStackHint(new URL(resolveCheckPath(definition.path), webBase()), undefined, "web app");
414
- const body = await response.text();
415
- const bodyText = visibleTextFromHtml(body);
416
- assertCondition(response.status === definition.expectStatus, \`web readiness expected \${definition.expectStatus}, got \${response.status}\`);
417
- assertCondition(bodyText.includes(definition.expectText), \`web readiness did not include expected text: \${definition.expectText}\`);
418
- if (definition.expectNotText) {
419
- assertCondition(!bodyText.includes(definition.expectNotText), \`web readiness unexpectedly included text: \${definition.expectNotText}\`);
420
- }
421
- } else if (definition.kind === "web_browser_contract") {
422
- const bodyText = await runSafariBrowserCheck(definition);
423
- if (definition.expectText) {
424
- assertCondition(bodyText.includes(definition.expectText), \`browser check did not include expected text: \${definition.expectText}\`);
425
- }
426
- if (definition.expectNotText) {
427
- assertCondition(!bodyText.includes(definition.expectNotText), \`browser check unexpectedly included text: \${definition.expectNotText}\`);
428
- }
429
- } else if (definition.kind === "api_health") {
430
- const response = await fetchWithStackHint(new URL(definition.path, apiBase()), undefined, "api service");
431
- const responseBody = await parseResponseBody(response);
432
- assertCondition(response.status === definition.expectStatus, \`api health expected \${definition.expectStatus}, got \${response.status}\`);
433
- assertCondition(responseBody?.ok === definition.expectOk, "api health did not report ok");
434
- } else if (definition.kind === "api_ready") {
435
- const response = await fetchWithStackHint(new URL(definition.path, apiBase()), undefined, "api service");
436
- const responseBody = await parseResponseBody(response);
437
- assertCondition(response.status === definition.expectStatus, \`api readiness expected \${definition.expectStatus}, got \${response.status}\`);
438
- assertCondition(responseBody?.ready === definition.expectReady, "api readiness did not report ready");
439
- ${runtimeCheckRenderers.renderRuntimeCheckCases()}
440
- } else {
441
- throw new Error(\`Unsupported runtime check id: \${definition.id}\`);
442
- }
443
- result.ok = true;
444
- } catch (error) {
445
- result.ok = false;
446
- result.error = error instanceof Error ? error.message : String(error);
447
- } finally {
448
- result.durationMs = Date.now() - startedAt;
449
- }
450
- return result;
451
- }
452
-
453
- function stageSummary(stageDefinition, checks) {
454
- const failedMandatory = checks.some((check) => check.ok === false && check.mandatory);
455
- const skipped = checks.every((check) => check.skipped);
456
- return {
457
- id: stageDefinition.id,
458
- name: stageDefinition.name,
459
- ok: skipped ? null : !failedMandatory,
460
- skipped,
461
- checks
462
- };
463
- }
464
-
465
- function skippedChecksForStage(stageDefinition, reason) {
466
- return stageDefinition.checks.map((definition) => ({
467
- ...checkResultTemplate(definition),
468
- skipped: true,
469
- error: reason
470
- }));
471
- }
472
-
473
- function logCheck(stageId, result) {
474
- const icon = result.skipped ? "-" : result.ok ? "[ok]" : "[fail]";
475
- const suffix = result.error ? \` - \${result.error}\` : "";
476
- console.log(\`\${icon} \${stageId}:\${result.id} (\${result.durationMs}ms)\${suffix}\`);
477
- }
478
-
479
- async function writeReport(report) {
480
- await fs.mkdir(path.dirname(reportPath), { recursive: true });
481
- await fs.writeFile(reportPath, \`\${JSON.stringify(report, null, 2)}\\n\`, "utf8");
482
- }
483
-
484
- const report = {
485
- ok: true,
486
- name: plan.name,
487
- reportPath: plan.report.outputPath,
488
- stages: [],
489
- generatedAt: new Date().toISOString()
490
- };
491
-
492
- let stopFurtherStages = false;
493
-
494
- for (const stageDefinition of plan.stages) {
495
- if (stopFurtherStages) {
496
- const skippedChecks = skippedChecksForStage(stageDefinition, "Skipped because a prior fail-fast stage failed");
497
- for (const check of skippedChecks) {
498
- logCheck(stageDefinition.id, check);
499
- }
500
- report.stages.push(stageSummary(stageDefinition, skippedChecks));
501
- continue;
502
- }
503
-
504
- const checks = [];
505
- for (const checkDefinition of stageDefinition.checks) {
506
- const result = await runCheck(checkDefinition);
507
- checks.push(result);
508
- logCheck(stageDefinition.id, result);
509
- if (stageDefinition.failFast && result.ok === false && result.mandatory) {
510
- stopFurtherStages = true;
511
- break;
512
- }
513
- }
514
-
515
- if (stopFurtherStages && checks.length < stageDefinition.checks.length) {
516
- const remaining = stageDefinition.checks.slice(checks.length).map((definition) => ({
517
- ...checkResultTemplate(definition),
518
- skipped: true,
519
- error: "Skipped because this stage failed in fail-fast mode"
520
- }));
521
- for (const check of remaining) {
522
- logCheck(stageDefinition.id, check);
523
- }
524
- checks.push(...remaining);
525
- }
526
-
527
- report.stages.push(stageSummary(stageDefinition, checks));
528
- }
529
-
530
- report.ok = report.stages.every((stage) => stage.ok !== false);
531
- await writeReport(report);
532
- console.log(JSON.stringify(report, null, 2));
533
-
534
- if (!report.ok) {
535
- process.exitCode = 1;
536
- }
537
- `;
538
- }
539
-
540
- export function generateRuntimeCheckBundle(graph, options = {}) {
541
- const plan = buildRuntimeCheckPlan(graph, options);
542
- return {
543
- ".env.example": renderRuntimeCheckEnvExample(graph, options),
544
- "README.md": renderRuntimeCheckReadme(graph, options),
545
- "runtime-check-plan.json": `${JSON.stringify(plan, null, 2)}\n`,
546
- "api-contracts.json": `${JSON.stringify(generateRuntimeApiContracts(graph), null, 2)}\n`,
547
- "scripts/check.sh": renderRuntimeCheckScript(),
548
- "scripts/check.mjs": renderRuntimeCheckModule(graph, options)
549
- };
550
- }
551
-
552
- export function generateRuntimeCheckPlan(graph, options = {}) {
553
- return buildRuntimeCheckPlan(graph, options);
554
- }
1
+ export {
2
+ generateRuntimeCheckBundle,
3
+ generateRuntimeCheckPlan
4
+ } from "./runtime-check/index.js";