codepot-gen 0.1.0__py3-none-any.whl

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 (264) hide show
  1. app/__init__.py +9 -0
  2. app/app.py +77 -0
  3. app/io/__init__.py +5 -0
  4. app/io/inference_output.py +20 -0
  5. app/models/__init__.py +35 -0
  6. app/models/diagnostics.py +15 -0
  7. app/models/events.py +22 -0
  8. app/models/inputs.py +45 -0
  9. app/models/outputs.py +108 -0
  10. app/workflows/__init__.py +7 -0
  11. app/workflows/emit.py +143 -0
  12. app/workflows/infer.py +102 -0
  13. app/workflows/inspect.py +99 -0
  14. app/workflows/template_paths.py +26 -0
  15. app/workflows/validate.py +142 -0
  16. cli/__init__.py +7 -0
  17. cli/bootstrap.py +24 -0
  18. cli/commands/__init__.py +6 -0
  19. cli/commands/emit.py +138 -0
  20. cli/commands/infer.py +92 -0
  21. cli/commands/inspect.py +73 -0
  22. cli/commands/validate.py +73 -0
  23. cli/constants/__init__.py +6 -0
  24. cli/constants/constants.py +42 -0
  25. cli/constants/defaults.py +41 -0
  26. cli/main.py +97 -0
  27. cli/paths.py +24 -0
  28. cli/presentation/__init__.py +5 -0
  29. cli/presentation/core/__init__.py +5 -0
  30. cli/presentation/core/console.py +59 -0
  31. cli/presentation/core/diagnostics.py +47 -0
  32. cli/presentation/core/interactive.py +135 -0
  33. cli/presentation/core/tables.py +139 -0
  34. cli/presentation/emit/__init__.py +5 -0
  35. cli/presentation/emit/diagnostics.py +7 -0
  36. cli/presentation/emit/files.py +19 -0
  37. cli/presentation/emit/renderer.py +29 -0
  38. cli/presentation/emit/status.py +22 -0
  39. cli/presentation/emit/summary.py +24 -0
  40. cli/presentation/infer/__init__.py +5 -0
  41. cli/presentation/infer/diagnostics.py +7 -0
  42. cli/presentation/infer/files.py +19 -0
  43. cli/presentation/infer/renderer.py +21 -0
  44. cli/presentation/infer/schemas.py +52 -0
  45. cli/presentation/infer/status.py +11 -0
  46. cli/presentation/infer/summary.py +28 -0
  47. cli/presentation/inspect/__init__.py +5 -0
  48. cli/presentation/inspect/diagnostics.py +7 -0
  49. cli/presentation/inspect/renderer.py +18 -0
  50. cli/presentation/inspect/resources.py +26 -0
  51. cli/presentation/inspect/status.py +11 -0
  52. cli/presentation/inspect/summary.py +27 -0
  53. cli/presentation/validate/__init__.py +5 -0
  54. cli/presentation/validate/diagnostics.py +7 -0
  55. cli/presentation/validate/issues.py +29 -0
  56. cli/presentation/validate/renderer.py +21 -0
  57. cli/presentation/validate/summary.py +17 -0
  58. codepot_gen/__init__.py +9 -0
  59. codepot_gen/templates/dart/.gitignore +5 -0
  60. codepot_gen/templates/dart/.prettierrc +5 -0
  61. codepot_gen/templates/dart/CHANGELOG.md.j2 +5 -0
  62. codepot_gen/templates/dart/README.md.j2 +55 -0
  63. codepot_gen/templates/dart/analysis_options.yaml.j2 +12 -0
  64. codepot_gen/templates/dart/lib/[project.name.path].dart.j2 +12 -0
  65. codepot_gen/templates/dart/paths.yaml +69 -0
  66. codepot_gen/templates/dart/pubspec.yaml.j2 +23 -0
  67. codepot_gen/templates/dart/{dto}/index.dart.j2 +1 -0
  68. codepot_gen/templates/dart/{dto}/model.dart.j2 +37 -0
  69. codepot_gen/templates/dart/{enum}/enum.dart.j2 +8 -0
  70. codepot_gen/templates/dart/{enum}/index.dart.j2 +1 -0
  71. codepot_gen/templates/dart/{feature}/[resource.name.path]_feature.dart.j2 +104 -0
  72. codepot_gen/templates/dart/{model}/index.dart.j2 +1 -0
  73. codepot_gen/templates/dart/{model}/model.dart.j2 +37 -0
  74. codepot_gen/templates/dart/{route_group}/[resource.name.path]_endpoints.dart.j2 +15 -0
  75. codepot_gen/templates/dart/{route_root}/routes.dart.j2 +11 -0
  76. codepot_gen/templates/dart/{version}/client.dart.j2 +22 -0
  77. codepot_gen/templates/dart/{version}/v1.dart.j2 +18 -0
  78. codepot_gen/templates/debug/README.md.j2 +38 -0
  79. codepot_gen/templates/debug/_partials/fields-table.md.j2 +0 -0
  80. codepot_gen/templates/debug/_partials/metadata.md.j2 +0 -0
  81. codepot_gen/templates/debug/_partials/operation-summary.md.j2 +0 -0
  82. codepot_gen/templates/debug/_partials/parameters-table.md.j2 +0 -0
  83. codepot_gen/templates/debug/_partials/request-body.md.j2 +0 -0
  84. codepot_gen/templates/debug/_partials/responses-table.md.j2 +0 -0
  85. codepot_gen/templates/debug/_partials/schema-summary.md.j2 +0 -0
  86. codepot_gen/templates/debug/paths.yaml +54 -0
  87. codepot_gen/templates/debug/{dto}/[dto.name.path].md.j2 +88 -0
  88. codepot_gen/templates/debug/{enum}/[enum.name.path].md.j2 +82 -0
  89. codepot_gen/templates/debug/{model}/[model.name.path].dependencies.md.j2 +23 -0
  90. codepot_gen/templates/debug/{model}/[model.name.path].fields.md.j2 +37 -0
  91. codepot_gen/templates/debug/{model}/[model.name.path].md.j2 +110 -0
  92. codepot_gen/templates/debug/{operation}/[operation.api.method]_[operation.name.path].md.j2 +45 -0
  93. codepot_gen/templates/debug/{resource}/index.md.j2 +57 -0
  94. codepot_gen/templates/debug/{resource}/operations.md.j2 +100 -0
  95. codepot_gen/templates/debug/{resource}/schemas.md.j2 +25 -0
  96. codepot_gen/templates/next/paths.yaml +112 -0
  97. codepot_gen/templates/next/{dto}/[dto.name.path.o].ts.j2 +26 -0
  98. codepot_gen/templates/next/{enum}/[enum.name.path.o].ts.j2 +38 -0
  99. codepot_gen/templates/next/{model}/[model.name.path.o].ts.j2 +26 -0
  100. codepot_gen/templates/next/{resource_dto}/index.ts.j2 +16 -0
  101. codepot_gen/templates/next/{resource_enums}/index.ts.j2 +16 -0
  102. codepot_gen/templates/next/{resource_models}/index.ts.j2 +16 -0
  103. codepot_gen/templates/next/{resource_types}/index.ts.j2 +28 -0
  104. codepot_gen/templates/next/{resource}/[resource.name.path.o].actions.ts.j2 +208 -0
  105. codepot_gen/templates/next/{root}/[project.meta.api_version].ts.j2 +12 -0
  106. codepot_gen/templates/next/{root}/actions/helpers.ts.j2 +176 -0
  107. codepot_gen/templates/next/{root}/actions/index.ts.j2 +11 -0
  108. codepot_gen/templates/next/{root}/api.ts.j2 +17 -0
  109. codepot_gen/templates/next/{root}/index.ts.j2 +8 -0
  110. codepot_gen/templates/next/{root}/next-actions.md +213 -0
  111. codepot_gen/templates/next/{root}/routes.ts.j2 +49 -0
  112. codepot_gen/templates/next/{root}/types/index.ts.j2 +10 -0
  113. codepot_gen/templates/next/{ui_page}/page.tsx.j2 +127 -0
  114. codepot_gen/templates/next/{ui_resource}/[resource.name.path.o]-columns.tsx.j2 +180 -0
  115. codepot_gen/templates/next/{ui_resource}/[resource.name.path.o]-dialogs.tsx.j2 +382 -0
  116. codepot_gen/templates/next/{ui_resource}/[resource.name.path.o]-form.tsx.j2 +270 -0
  117. codepot_gen/templates/next/{ui_resource}/[resource.name.path.o]-table.tsx.j2 +87 -0
  118. codepot_gen/templates/next/{ui_resource}/index.ts.j2 +12 -0
  119. codepot_gen/templates/typescript/.gitignore +40 -0
  120. codepot_gen/templates/typescript/.prettierrc +5 -0
  121. codepot_gen/templates/typescript/README.md.j2 +21 -0
  122. codepot_gen/templates/typescript/package.json.j2 +16 -0
  123. codepot_gen/templates/typescript/paths.yaml +72 -0
  124. codepot_gen/templates/typescript/tsconfig.json.j2 +14 -0
  125. codepot_gen/templates/typescript/{dto}/dto.ts.j2 +9 -0
  126. codepot_gen/templates/typescript/{enum}/enum.ts.j2 +5 -0
  127. codepot_gen/templates/typescript/{model}/model.ts.j2 +9 -0
  128. codepot_gen/templates/typescript/{operation}/operation.ts.j2 +5 -0
  129. codepot_gen/templates/typescript/{version}/index.ts.j2 +12 -0
  130. codepot_gen-0.1.0.dist-info/METADATA +106 -0
  131. codepot_gen-0.1.0.dist-info/RECORD +264 -0
  132. codepot_gen-0.1.0.dist-info/WHEEL +5 -0
  133. codepot_gen-0.1.0.dist-info/entry_points.txt +4 -0
  134. codepot_gen-0.1.0.dist-info/top_level.txt +11 -0
  135. constants/__init__.py +7 -0
  136. constants/app.py +6 -0
  137. constants/codegen.py +39 -0
  138. constants/emission.py +38 -0
  139. constants/files.py +17 -0
  140. constants/http.py +25 -0
  141. constants/openapi.py +116 -0
  142. contracts/__init__.py +124 -0
  143. contracts/api.py +318 -0
  144. contracts/emission.py +55 -0
  145. contracts/events.py +33 -0
  146. contracts/language.py +61 -0
  147. contracts/names.py +15 -0
  148. contracts/path_yaml.py +217 -0
  149. contracts/paths.py +127 -0
  150. contracts/template.py +601 -0
  151. core/__init__.py +7 -0
  152. core/config.py +47 -0
  153. core/errors.py +21 -0
  154. core/paths.py +79 -0
  155. emission/__init__.py +5 -0
  156. emission/dependencies/output_index.py +80 -0
  157. emission/dependencies/resolver.py +70 -0
  158. emission/engine.py +497 -0
  159. emission/imports/base.py +29 -0
  160. emission/imports/markdown.py +48 -0
  161. emission/imports/paths.py +18 -0
  162. emission/paths/__init__.py +5 -0
  163. emission/paths/config_loader.py +23 -0
  164. emission/paths/selection.py +59 -0
  165. emission/templates/__init__.py +5 -0
  166. emission/templates/descriptor.py +59 -0
  167. emission/templates/path_expander.py +103 -0
  168. emission/templates/path_safety.py +64 -0
  169. emission/templates/path_tokens.py +143 -0
  170. emission/templates/renderer.py +90 -0
  171. emission/templates/resolver.py +67 -0
  172. emission/templates/scanner.py +33 -0
  173. emission/writer/__init__.py +0 -0
  174. emission/writer/file_writer.py +100 -0
  175. inference/__init__.py +10 -0
  176. inference/classifiers.py +96 -0
  177. inference/contract.py +448 -0
  178. inference/engine.py +116 -0
  179. inference/graph.py +51 -0
  180. inference/metadata/__init__.py +5 -0
  181. inference/metadata/parameters.py +71 -0
  182. inference/metadata/query.py +43 -0
  183. inference/metadata/targets.py +62 -0
  184. inference/metadata/ui.py +61 -0
  185. inference/models/__init__.py +35 -0
  186. inference/models/base.py +13 -0
  187. inference/models/dependencies.py +10 -0
  188. inference/models/graph.py +24 -0
  189. inference/models/operations.py +90 -0
  190. inference/models/resources.py +11 -0
  191. inference/models/schemas.py +87 -0
  192. inference/operations/__init__.py +7 -0
  193. inference/operations/dependencies.py +32 -0
  194. inference/operations/engine.py +198 -0
  195. inference/operations/parameter_targets.py +49 -0
  196. inference/operations/parameters.py +59 -0
  197. inference/operations/request_bodies.py +70 -0
  198. inference/operations/resources.py +17 -0
  199. inference/operations/responses.py +85 -0
  200. inference/ref_metadata.py +81 -0
  201. inference/resources.py +48 -0
  202. inference/schemas/__init__.py +251 -0
  203. inference/schemas/composition.py +193 -0
  204. inference/schemas/enums.py +53 -0
  205. inference/schemas/field_types.py +143 -0
  206. inference/schemas/fields.py +166 -0
  207. inference/schemas/primitives.py +52 -0
  208. inference/schemas/resolution.py +115 -0
  209. inference/serialization.py +10 -0
  210. languages/__init__.py +5 -0
  211. languages/dart/__init__.py +6 -0
  212. languages/dart/adapter.py +185 -0
  213. languages/dart/constants.py +16 -0
  214. languages/dart/context.py +67 -0
  215. languages/dart/dependencies.py +229 -0
  216. languages/dart/fields.py +87 -0
  217. languages/dart/imports.py +193 -0
  218. languages/dart/names.py +218 -0
  219. languages/dart/operations.py +500 -0
  220. languages/dart/paths.py +39 -0
  221. languages/dart/resources.py +108 -0
  222. languages/dart/schemas.py +276 -0
  223. languages/dart/types.py +275 -0
  224. languages/dart/urls.py +32 -0
  225. languages/debug/__init__.py +5 -0
  226. languages/debug/adapter.py +896 -0
  227. languages/debug/context/__init__.py +2 -0
  228. languages/debug/context/path_values.py +23 -0
  229. languages/decorators.py +88 -0
  230. languages/discovery.py +51 -0
  231. languages/typescript/__init__.py +6 -0
  232. languages/typescript/adapter.py +178 -0
  233. languages/typescript/constants.py +18 -0
  234. languages/typescript/context.py +61 -0
  235. languages/typescript/dependencies.py +228 -0
  236. languages/typescript/fields.py +170 -0
  237. languages/typescript/imports.py +210 -0
  238. languages/typescript/names.py +259 -0
  239. languages/typescript/operations.py +700 -0
  240. languages/typescript/paths.py +39 -0
  241. languages/typescript/resources.py +113 -0
  242. languages/typescript/schemas.py +243 -0
  243. languages/typescript/types.py +269 -0
  244. languages/typescript/urls.py +20 -0
  245. openapi/__init__.py +9 -0
  246. openapi/document.py +88 -0
  247. openapi/inspector.py +126 -0
  248. openapi/loader.py +71 -0
  249. openapi/ref_metadata.py +67 -0
  250. openapi/refs.py +88 -0
  251. openapi/resolver/__init__.py +28 -0
  252. openapi/resolver/components.py +104 -0
  253. openapi/resolver/content.py +81 -0
  254. openapi/resolver/parameters.py +110 -0
  255. openapi/resolver/pointers.py +69 -0
  256. openapi/resolver/request_bodies.py +66 -0
  257. openapi/resolver/responses.py +73 -0
  258. utils/__init__.py +1 -0
  259. utils/naming/__init__.py +17 -0
  260. utils/naming/aliases.py +18 -0
  261. utils/naming/cases.py +80 -0
  262. utils/naming/number.py +25 -0
  263. utils/naming/plurality.py +126 -0
  264. utils/naming/provider.py +188 -0
app/__init__.py ADDED
@@ -0,0 +1,9 @@
1
+ """Application orchestration package.
2
+
3
+ This package exposes the public GeneratorApp API and app workflows used by CLI,
4
+ tests, UI adapters, or other external interfaces.
5
+ """
6
+
7
+ from .app import GeneratorApp
8
+
9
+ __all__ = ["GeneratorApp"]
app/app.py ADDED
@@ -0,0 +1,77 @@
1
+ """Public runtime application API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from app.models import (
8
+ EmitInput,
9
+ EmitOutput,
10
+ InferInput,
11
+ InferOutput,
12
+ InspectInput,
13
+ InspectOutput,
14
+ ValidateInput,
15
+ ValidateOutput,
16
+ )
17
+ from app.workflows.emit import run_emit
18
+ from app.workflows.infer import run_infer
19
+ from app.workflows.inspect import run_inspect
20
+ from app.workflows.validate import run_validate
21
+
22
+
23
+ class GeneratorApp:
24
+ """Public runtime API for the generator.
25
+
26
+ Interfaces such as CLI, UI, tests, or HTTP handlers should call this class.
27
+ Runtime methods return structured results and do not render terminal output.
28
+ """
29
+
30
+ def inspect(self, input_path: Path) -> InspectOutput:
31
+ """Inspect an OpenAPI document."""
32
+ return run_inspect(
33
+ InspectInput(
34
+ input_path=input_path,
35
+ )
36
+ )
37
+
38
+ def infer(
39
+ self,
40
+ input_path: Path,
41
+ output_path: Path | None = None,
42
+ ) -> InferOutput:
43
+ """Run OpenAPI inference."""
44
+ return run_infer(
45
+ InferInput(
46
+ input_path=input_path,
47
+ output_path=output_path,
48
+ )
49
+ )
50
+
51
+ def emit(
52
+ self,
53
+ input_path: Path,
54
+ language: str,
55
+ output_path: Path,
56
+ *,
57
+ dry_run: bool = False,
58
+ templates_path: Path | None = None,
59
+ ) -> EmitOutput:
60
+ """Emit generated output for a language."""
61
+ return run_emit(
62
+ EmitInput(
63
+ input_path=input_path,
64
+ language=language,
65
+ output_path=output_path,
66
+ dry_run=dry_run,
67
+ templates_path=templates_path,
68
+ )
69
+ )
70
+
71
+ def validate(self, input_path: Path) -> ValidateOutput:
72
+ """Validate an OpenAPI document."""
73
+ return run_validate(
74
+ ValidateInput(
75
+ input_path=input_path,
76
+ )
77
+ )
app/io/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Runtime file I/O helpers.
2
+
3
+ This package contains serialization and file-writing helpers used by runtime
4
+ workflows. It must not render terminal output or import CLI presentation code.
5
+ """
@@ -0,0 +1,20 @@
1
+ """Inference graph output serialization."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+ from constants.files import ENCODING_UTF8
9
+ from inference.models import InferenceGraph
10
+ from inference.serialization import inference_graph_to_dict
11
+
12
+
13
+ def write_inference_graph(graph: InferenceGraph, output_path: Path) -> Path:
14
+ """Write an inference graph as JSON and return the written path."""
15
+ output_path.parent.mkdir(parents=True, exist_ok=True)
16
+ output_path.write_text(
17
+ json.dumps(inference_graph_to_dict(graph), indent=2, default=str),
18
+ encoding=ENCODING_UTF8,
19
+ )
20
+ return output_path
app/models/__init__.py ADDED
@@ -0,0 +1,35 @@
1
+ """Runtime input and output contracts.
2
+
3
+ These models define the public runtime interface used by CLI, tests, UI, API
4
+ servers, and other adapters.
5
+ """
6
+
7
+ from app.models.diagnostics import RuntimeDiagnostic
8
+ from app.models.events import ProgressSink, RuntimeEvent
9
+ from app.models.inputs import EmitInput, InferInput, InspectInput, ValidateInput
10
+ from app.models.outputs import (
11
+ AliasSchemaSummary,
12
+ EmitOutput,
13
+ InferOutput,
14
+ InspectOutput,
15
+ ResourceSummary,
16
+ UnknownSchemaSummary,
17
+ ValidateOutput,
18
+ )
19
+
20
+ __all__ = [
21
+ "EmitInput",
22
+ "InferInput",
23
+ "InspectInput",
24
+ "ValidateInput",
25
+ "EmitOutput",
26
+ "InferOutput",
27
+ "InspectOutput",
28
+ "ValidateOutput",
29
+ "ResourceSummary",
30
+ "UnknownSchemaSummary",
31
+ "AliasSchemaSummary",
32
+ "RuntimeDiagnostic",
33
+ "RuntimeEvent",
34
+ "ProgressSink",
35
+ ]
@@ -0,0 +1,15 @@
1
+ """Runtime diagnostic models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Any
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class RuntimeDiagnostic:
11
+ """A structured diagnostic emitted by runtime workflows."""
12
+
13
+ level: str
14
+ message: str
15
+ details: dict[str, Any] = field(default_factory=dict)
app/models/events.py ADDED
@@ -0,0 +1,22 @@
1
+ """Runtime progress event models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass, field
7
+ from typing import Any
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class RuntimeEvent:
12
+ """A structured runtime progress event."""
13
+
14
+ stage: str
15
+ message: str
16
+ level: str = "info"
17
+ current: int | None = None
18
+ total: int | None = None
19
+ details: dict[str, Any] = field(default_factory=dict)
20
+
21
+
22
+ ProgressSink = Callable[[RuntimeEvent], None]
app/models/inputs.py ADDED
@@ -0,0 +1,45 @@
1
+ """Runtime input contracts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+
8
+ from app.models.events import ProgressSink
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class InspectInput:
13
+ """Input for inspecting an OpenAPI document."""
14
+
15
+ input_path: Path
16
+ progress: ProgressSink | None = None
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class ValidateInput:
21
+ """Input for validating an OpenAPI document."""
22
+
23
+ input_path: Path
24
+ progress: ProgressSink | None = None
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class InferInput:
29
+ """Input for OpenAPI inference."""
30
+
31
+ input_path: Path
32
+ output_path: Path | None = None
33
+ progress: ProgressSink | None = None
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class EmitInput:
38
+ """Input for emitting generated output."""
39
+
40
+ input_path: Path
41
+ language: str
42
+ output_path: Path
43
+ dry_run: bool = False
44
+ templates_path: Path | None = None
45
+ progress: ProgressSink | None = None
app/models/outputs.py ADDED
@@ -0,0 +1,108 @@
1
+ """Runtime output contracts."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from app.models.diagnostics import RuntimeDiagnostic
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class ResourceSummary:
14
+ """Detected resource summary."""
15
+
16
+ name: str
17
+ path: str = "-"
18
+ operations_count: int = 0
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class UnknownSchemaSummary:
23
+ """Unknown schema summary for inference diagnostics."""
24
+
25
+ name: str
26
+ ref: str
27
+ x_codegen_kind: str = "-"
28
+ keys: list[str] = field(default_factory=list)
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ class AliasSchemaSummary:
33
+ """Alias schema summary for inference diagnostics."""
34
+
35
+ name: str
36
+ kind: str
37
+ alias_of: str = "-"
38
+ resource: str = "-"
39
+
40
+
41
+ @dataclass(frozen=True)
42
+ class InspectOutput:
43
+ """Output from OpenAPI inspection."""
44
+
45
+ input_path: Path
46
+ title: str = "-"
47
+ openapi_version: str = "-"
48
+ api_version: str = "-"
49
+ paths_count: int = 0
50
+ operations_count: int = 0
51
+ schemas_count: int = 0
52
+ responses_count: int = 0
53
+ request_bodies_count: int = 0
54
+ parameters_count: int = 0
55
+ refs_count: int = 0
56
+ component_refs_count: int = 0
57
+ missing_component_refs_count: int = 0
58
+ resources: list[ResourceSummary] = field(default_factory=list)
59
+ diagnostics: list[RuntimeDiagnostic] = field(default_factory=list)
60
+
61
+
62
+ @dataclass(frozen=True)
63
+ class ValidateOutput:
64
+ """Output from OpenAPI validation."""
65
+
66
+ input_path: Path
67
+ valid: bool
68
+ errors: list[str] = field(default_factory=list)
69
+ warnings: list[str] = field(default_factory=list)
70
+ diagnostics: list[RuntimeDiagnostic] = field(default_factory=list)
71
+
72
+
73
+ @dataclass(frozen=True)
74
+ class InferOutput:
75
+ """Output from OpenAPI inference."""
76
+
77
+ input_path: Path
78
+ output_path: Path | None = None
79
+ graph: Any | None = None
80
+ title: str = "-"
81
+ openapi_version: str = "-"
82
+ api_version: str = "-"
83
+ resources_count: int = 0
84
+ schemas_count: int = 0
85
+ operations_count: int = 0
86
+ dependencies_count: int = 0
87
+ alias_schemas_count: int = 0
88
+ schema_kind_counts: dict[str, int] = field(default_factory=dict)
89
+ unknown_schemas: list[UnknownSchemaSummary] = field(default_factory=list)
90
+ alias_schemas: list[AliasSchemaSummary] = field(default_factory=list)
91
+ written: list[Path] = field(default_factory=list)
92
+ diagnostics: list[RuntimeDiagnostic] = field(default_factory=list)
93
+
94
+
95
+ @dataclass(frozen=True)
96
+ class EmitOutput:
97
+ """Output from code/text emission."""
98
+
99
+ input_path: Path
100
+ language: str
101
+ output_path: Path
102
+ dry_run: bool = False
103
+ planned: list[Path] = field(default_factory=list)
104
+ written: list[Path] = field(default_factory=list)
105
+ updated: list[Path] = field(default_factory=list)
106
+ unchanged: list[Path] = field(default_factory=list)
107
+ skipped: list[Path] = field(default_factory=list)
108
+ diagnostics: list[RuntimeDiagnostic] = field(default_factory=list)
@@ -0,0 +1,7 @@
1
+ """
2
+ Command workflow orchestration package.
3
+
4
+ This package contains workflow functions that orchestrate the full pipeline for
5
+ each CLI command: inspect, infer, emit, and validate. Each workflow loads documents,
6
+ runs inference, creates emission plans, and delegates to presenters for output.
7
+ """
app/workflows/emit.py ADDED
@@ -0,0 +1,143 @@
1
+ """App emit workflow.
2
+
3
+ Orchestrates the full emission pipeline: OpenAPI loading, inference, contract
4
+ building, language adaptation, and template emission.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from app.models import EmitInput, EmitOutput, RuntimeDiagnostic, RuntimeEvent
10
+ from app.workflows.template_paths import resolve_template_root
11
+ from emission.engine import emit as run_emission
12
+ from inference.contract import build_api_contract
13
+ from inference.engine import InferenceEngine
14
+ from languages.discovery import resolve_language_adapter
15
+ from openapi.loader import load_openapi_document
16
+
17
+
18
+ def run_emit(request: EmitInput) -> EmitOutput:
19
+ """Run the emit workflow and return structured output."""
20
+ _notify(
21
+ request,
22
+ stage="loading_openapi",
23
+ message=f"Loading OpenAPI document from {request.input_path}",
24
+ )
25
+ document = load_openapi_document(request.input_path)
26
+
27
+ _notify(
28
+ request,
29
+ stage="running_inference",
30
+ message="Running OpenAPI inference",
31
+ )
32
+ graph = InferenceEngine().infer(document)
33
+
34
+ _notify(
35
+ request,
36
+ stage="building_contract",
37
+ message="Building API contract",
38
+ )
39
+ api_contract = build_api_contract(graph)
40
+
41
+ _notify(
42
+ request,
43
+ stage="resolving_language",
44
+ message=f"Resolving language adapter: {request.language}",
45
+ )
46
+ adapter = resolve_language_adapter(request.language)
47
+ template_root = resolve_template_root(
48
+ adapter=adapter,
49
+ templates_path=request.templates_path,
50
+ )
51
+
52
+ _notify(
53
+ request,
54
+ stage="building_template_contract",
55
+ message=f"Building template contract from {template_root}",
56
+ )
57
+ template_contract = adapter.build_template_contract(
58
+ api=api_contract,
59
+ output_path=request.output_path,
60
+ template_root=template_root,
61
+ dry_run=request.dry_run,
62
+ progress=request.progress,
63
+ )
64
+
65
+ _notify(
66
+ request,
67
+ stage="emitting_files",
68
+ message="Emitting files",
69
+ )
70
+ emission_result = run_emission(
71
+ template_contract,
72
+ progress=request.progress,
73
+ )
74
+
75
+ _notify(
76
+ request,
77
+ stage="language_post_actions",
78
+ message="Running language post-actions",
79
+ )
80
+ post_result = adapter.after_emit(
81
+ result=emission_result,
82
+ progress=request.progress,
83
+ )
84
+
85
+ _notify(
86
+ request,
87
+ stage="emission_complete",
88
+ message="Emission completed",
89
+ total=len(emission_result.plan.files),
90
+ )
91
+
92
+ write_result = emission_result.write_result
93
+ diagnostics = [
94
+ RuntimeDiagnostic(
95
+ level="info",
96
+ message=(
97
+ "Emission completed: "
98
+ f"{len(write_result.created)} created, "
99
+ f"{len(write_result.updated)} updated, "
100
+ f"{len(write_result.unchanged)} unchanged, "
101
+ f"{len(write_result.skipped)} skipped."
102
+ ),
103
+ )
104
+ ]
105
+
106
+ diagnostics.extend(RuntimeDiagnostic(level="info", message=message) for message in post_result.diagnostics) # noqa: E501
107
+
108
+ return EmitOutput(
109
+ input_path=request.input_path,
110
+ language=request.language,
111
+ output_path=request.output_path,
112
+ dry_run=request.dry_run,
113
+ planned=[file.output_path for file in emission_result.plan.files],
114
+ written=list(write_result.created),
115
+ updated=list(write_result.updated),
116
+ unchanged=list(write_result.unchanged),
117
+ skipped=list(write_result.skipped),
118
+ diagnostics=diagnostics,
119
+ )
120
+
121
+
122
+ def _notify(
123
+ request: EmitInput,
124
+ *,
125
+ stage: str,
126
+ message: str,
127
+ level: str = "info",
128
+ current: int | None = None,
129
+ total: int | None = None,
130
+ ) -> None:
131
+ """Emit a runtime progress event when a sink is provided."""
132
+ if request.progress is None:
133
+ return
134
+
135
+ request.progress(
136
+ RuntimeEvent(
137
+ stage=stage,
138
+ message=message,
139
+ level=level,
140
+ current=current,
141
+ total=total,
142
+ )
143
+ )
app/workflows/infer.py ADDED
@@ -0,0 +1,102 @@
1
+ """Runtime inference workflow."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections import Counter
6
+
7
+ from inference.engine import InferenceEngine
8
+ from openapi.loader import load_openapi_document
9
+ from app.io.inference_output import write_inference_graph
10
+ from app.models import (
11
+ AliasSchemaSummary,
12
+ InferInput,
13
+ InferOutput,
14
+ RuntimeDiagnostic,
15
+ RuntimeEvent,
16
+ UnknownSchemaSummary,
17
+ )
18
+
19
+
20
+ def run_infer(request: InferInput) -> InferOutput:
21
+ """Run inference and return structured output."""
22
+ _notify(request, stage="loading_openapi", message="Loading OpenAPI document")
23
+ document = load_openapi_document(request.input_path)
24
+
25
+ _notify(request, stage="running_inference", message="Running inference engine")
26
+ graph = InferenceEngine().infer(document)
27
+
28
+ written = []
29
+ if request.output_path is not None:
30
+ _notify(
31
+ request,
32
+ stage="writing_inference_output",
33
+ message=f"Writing inference graph to {request.output_path}",
34
+ )
35
+ written.append(write_inference_graph(graph, request.output_path))
36
+
37
+ schemas = list(getattr(graph, "schemas", []) or [])
38
+ alias_schemas = [schema for schema in schemas if bool(getattr(schema, "is_alias", False))]
39
+ unknown_schemas = [schema for schema in schemas if getattr(getattr(schema, "kind", None), "value", "") == "unknown"]
40
+ kind_counts = Counter(str(getattr(getattr(schema, "kind", None), "value", "-")) for schema in schemas)
41
+
42
+ _notify(request, stage="inference_complete", message="Inference completed")
43
+
44
+ return InferOutput(
45
+ input_path=request.input_path,
46
+ output_path=request.output_path,
47
+ graph=graph,
48
+ title=str(getattr(graph, "title", "-")),
49
+ openapi_version=str(getattr(graph, "openapi_version", "-")),
50
+ api_version=str(getattr(graph, "api_version", "-")),
51
+ resources_count=len(list(getattr(graph, "resources", []) or [])),
52
+ schemas_count=len(schemas),
53
+ operations_count=len(list(getattr(graph, "operations", []) or [])),
54
+ dependencies_count=len(list(getattr(graph, "dependencies", []) or [])),
55
+ alias_schemas_count=len(alias_schemas),
56
+ schema_kind_counts=dict(sorted(kind_counts.items())),
57
+ unknown_schemas=[
58
+ UnknownSchemaSummary(
59
+ name=str(getattr(schema, "name", "-")),
60
+ ref=str(getattr(schema, "ref", "-")),
61
+ x_codegen_kind=str(
62
+ getattr(schema, "x_codegen", {}).get("kind", "-") if isinstance(getattr(schema, "x_codegen", {}), dict) else "-"
63
+ ),
64
+ keys=sorted(list(getattr(schema, "raw", {}).keys())) if isinstance(getattr(schema, "raw", {}), dict) else [],
65
+ )
66
+ for schema in unknown_schemas
67
+ ],
68
+ alias_schemas=[
69
+ AliasSchemaSummary(
70
+ name=str(getattr(schema, "name", "-")),
71
+ kind=str(getattr(getattr(schema, "kind", None), "value", "-")),
72
+ alias_of=str(getattr(schema, "alias_of", None) or "-"),
73
+ resource=str(getattr(getattr(schema, "resource", None), "name", "-")),
74
+ )
75
+ for schema in alias_schemas
76
+ ],
77
+ written=written,
78
+ diagnostics=[RuntimeDiagnostic(level="info", message="Inference completed.")],
79
+ )
80
+
81
+
82
+ def _notify(
83
+ request: InferInput,
84
+ *,
85
+ stage: str,
86
+ message: str,
87
+ level: str = "info",
88
+ current: int | None = None,
89
+ total: int | None = None,
90
+ ) -> None:
91
+ if request.progress is None:
92
+ return
93
+
94
+ request.progress(
95
+ RuntimeEvent(
96
+ stage=stage,
97
+ message=message,
98
+ level=level,
99
+ current=current,
100
+ total=total,
101
+ )
102
+ )
@@ -0,0 +1,99 @@
1
+ """Runtime OpenAPI inspection workflow."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from openapi.inspector import inspect_openapi_document
6
+ from openapi.loader import load_openapi_document
7
+ from app.models import InspectInput, InspectOutput, ResourceSummary, RuntimeDiagnostic, RuntimeEvent
8
+
9
+
10
+ def run_inspect(request: InspectInput) -> InspectOutput:
11
+ """Inspect an OpenAPI document and return structured output."""
12
+ _notify(request, stage="loading_openapi", message="Loading OpenAPI document")
13
+ document = load_openapi_document(request.input_path)
14
+
15
+ _notify(request, stage="inspecting_openapi", message="Inspecting OpenAPI document")
16
+ inspection = inspect_openapi_document(document)
17
+
18
+ _notify(request, stage="inspection_complete", message="OpenAPI inspection completed")
19
+
20
+ return InspectOutput(
21
+ input_path=request.input_path,
22
+ title=str(_value(inspection, "title")),
23
+ openapi_version=str(_value(inspection, "openapi_version")),
24
+ api_version=str(_value(inspection, "api_version")),
25
+ paths_count=_int_value(inspection, "path_count", "paths_count"),
26
+ operations_count=_int_value(inspection, "operation_count", "operations_count"),
27
+ schemas_count=_int_value(inspection, "schema_count", "schemas_count"),
28
+ responses_count=_int_value(inspection, "response_count", "responses_count"),
29
+ request_bodies_count=_int_value(
30
+ inspection,
31
+ "request_body_count",
32
+ "request_bodies_count",
33
+ ),
34
+ parameters_count=_int_value(inspection, "parameter_count", "parameters_count"),
35
+ refs_count=_int_value(inspection, "ref_count", "refs_count"),
36
+ component_refs_count=_int_value(
37
+ inspection,
38
+ "component_ref_count",
39
+ "component_refs_count",
40
+ ),
41
+ missing_component_refs_count=_int_value(
42
+ inspection,
43
+ "missing_component_ref_count",
44
+ "missing_component_refs_count",
45
+ ),
46
+ resources=[
47
+ ResourceSummary(
48
+ name=str(_value(resource, "name")),
49
+ path="/".join(_value(resource, "path", default=[])) or "-",
50
+ operations_count=_int_value(resource, "operation_count", "operations_count"),
51
+ )
52
+ for resource in _items(inspection, "resources")
53
+ ],
54
+ diagnostics=[RuntimeDiagnostic(level="info", message="OpenAPI inspection completed.")],
55
+ )
56
+
57
+
58
+ def _items(value, name: str) -> list:
59
+ result = getattr(value, name, [])
60
+ return list(result or [])
61
+
62
+
63
+ def _value(value, *names: str, default="-"):
64
+ for name in names:
65
+ if hasattr(value, name):
66
+ result = getattr(value, name)
67
+ return default if result is None else result
68
+ return default
69
+
70
+
71
+ def _int_value(value, *names: str) -> int:
72
+ result = _value(value, *names, default=0)
73
+ try:
74
+ return int(result)
75
+ except (TypeError, ValueError):
76
+ return 0
77
+
78
+
79
+ def _notify(
80
+ request: InspectInput,
81
+ *,
82
+ stage: str,
83
+ message: str,
84
+ level: str = "info",
85
+ current: int | None = None,
86
+ total: int | None = None,
87
+ ) -> None:
88
+ if request.progress is None:
89
+ return
90
+
91
+ request.progress(
92
+ RuntimeEvent(
93
+ stage=stage,
94
+ message=message,
95
+ level=level,
96
+ current=current,
97
+ total=total,
98
+ )
99
+ )