@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,785 +1,17 @@
1
1
  // @ts-check
2
2
 
3
- import fs from "node:fs";
4
- import path from "node:path";
5
-
6
- import { stableStringify } from "../../format.js";
7
- import {
8
- defaultGeneratorPolicy,
9
- GENERATOR_POLICY_FILE,
10
- generatorPackageAllowed,
11
- generatorPolicyDiagnosticsForBindings,
12
- loadGeneratorPolicy,
13
- packageBackedGeneratorBindings,
14
- packageScopeFromName,
15
- parseGeneratorPolicyPin,
16
- writeGeneratorPolicy
17
- } from "../../generator-policy.js";
18
- import { loadProjectConfig } from "../../project-config.js";
19
-
20
- /**
21
- * @param {string} name
22
- * @param {boolean} ok
23
- * @param {string} actual
24
- * @param {string} expected
25
- * @param {string} message
26
- * @param {string|null} fix
27
- * @returns {{ name: string, ok: boolean, actual: string, expected: string, message: string, fix: string|null }}
28
- */
29
- function generatorPolicyRule(name, ok, actual, expected, message, fix = null) {
30
- return { name, ok, actual, expected, message, fix };
31
- }
32
-
33
- /**
34
- * @param {string} name
35
- * @returns {string}
36
- */
37
- function generatorPolicyRuleLabel(name) {
38
- return ({
39
- "policy-file": "Policy file",
40
- "allowed-package": "Allowed package",
41
- "pinned-version": "Pinned version"
42
- })[name] || name;
43
- }
44
-
45
- /**
46
- * @param {any} policyInfo
47
- * @returns {any}
48
- */
49
- function effectiveGeneratorPolicy(policyInfo) {
50
- return policyInfo.policy || {
51
- version: "0.1",
52
- allowedPackageScopes: ["@topogram"],
53
- allowedPackages: [],
54
- pinnedVersions: {}
55
- };
56
- }
57
-
58
- /**
59
- * @param {string} filePath
60
- * @returns {any|null}
61
- */
62
- function readJsonIfPresent(filePath) {
63
- if (!fs.existsSync(filePath)) {
64
- return null;
65
- }
66
- try {
67
- return JSON.parse(fs.readFileSync(filePath, "utf8"));
68
- } catch {
69
- return null;
70
- }
71
- }
72
-
73
- /**
74
- * @param {Record<string, any>|null} projectPackage
75
- * @param {string} packageName
76
- * @returns {{ field: string|null, spec: string|null }}
77
- */
78
- function dependencySpecForPackage(projectPackage, packageName) {
79
- const dependencyFields = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
80
- for (const field of dependencyFields) {
81
- const dependencies = projectPackage?.[field];
82
- if (dependencies && typeof dependencies === "object" && typeof dependencies[packageName] === "string") {
83
- return {
84
- field,
85
- spec: dependencies[packageName]
86
- };
87
- }
88
- }
89
- return {
90
- field: null,
91
- spec: null
92
- };
93
- }
94
-
95
- /**
96
- * @param {Record<string, any>|null} lockfile
97
- * @param {string} packageName
98
- * @returns {{ version: string|null, resolved: string|null, integrity: string|null, entryPath: string|null }}
99
- */
100
- function npmLockfileInfoForPackage(lockfile, packageName) {
101
- const packageEntryPath = `node_modules/${packageName}`;
102
- const packageEntry = lockfile?.packages?.[packageEntryPath];
103
- if (packageEntry && typeof packageEntry === "object") {
104
- return {
105
- version: typeof packageEntry.version === "string" ? packageEntry.version : null,
106
- resolved: typeof packageEntry.resolved === "string" ? packageEntry.resolved : null,
107
- integrity: typeof packageEntry.integrity === "string" ? packageEntry.integrity : null,
108
- entryPath: packageEntryPath
109
- };
110
- }
111
- const dependencyEntry = lockfile?.dependencies?.[packageName];
112
- if (dependencyEntry && typeof dependencyEntry === "object") {
113
- return {
114
- version: typeof dependencyEntry.version === "string" ? dependencyEntry.version : null,
115
- resolved: typeof dependencyEntry.resolved === "string" ? dependencyEntry.resolved : null,
116
- integrity: typeof dependencyEntry.integrity === "string" ? dependencyEntry.integrity : null,
117
- entryPath: packageName
118
- };
119
- }
120
- return {
121
- version: null,
122
- resolved: null,
123
- integrity: null,
124
- entryPath: null
125
- };
126
- }
127
-
128
- /**
129
- * @param {string} projectRoot
130
- * @returns {{ kind: "npm"|"pnpm"|"yarn"|"bun"|null, path: string|null, data: Record<string, any>|null, note: string|null }}
131
- */
132
- function lockfileMetadataForProject(projectRoot) {
133
- const npmLockfilePath = path.join(projectRoot, "package-lock.json");
134
- if (fs.existsSync(npmLockfilePath)) {
135
- return {
136
- kind: "npm",
137
- path: npmLockfilePath,
138
- data: readJsonIfPresent(npmLockfilePath),
139
- note: null
140
- };
141
- }
142
- const candidates = [
143
- { kind: "pnpm", file: "pnpm-lock.yaml" },
144
- { kind: "yarn", file: "yarn.lock" },
145
- { kind: "bun", file: "bun.lock" },
146
- { kind: "bun", file: "bun.lockb" }
147
- ];
148
- for (const candidate of candidates) {
149
- const lockfilePath = path.join(projectRoot, candidate.file);
150
- if (fs.existsSync(lockfilePath)) {
151
- return {
152
- kind: /** @type {"pnpm"|"yarn"|"bun"} */ (candidate.kind),
153
- path: lockfilePath,
154
- data: null,
155
- note: `${candidate.file} found; package versions are not inspected by this command.`
156
- };
157
- }
158
- }
159
- return {
160
- kind: null,
161
- path: null,
162
- data: null,
163
- note: null
164
- };
165
- }
166
-
167
- /**
168
- * @param {string} projectRoot
169
- * @param {string} packageName
170
- * @returns {{ dependencyField: string|null, dependencySpec: string|null, installedVersion: string|null, installedPackageJsonPath: string|null, lockfileKind: "npm"|"pnpm"|"yarn"|"bun"|null, lockfilePath: string|null, lockfileVersion: string|null, lockfileResolved: string|null, lockfileIntegrity: string|null, lockfileEntryPath: string|null, lockfileNote: string|null }}
171
- */
172
- function packageInfoForGenerator(projectRoot, packageName) {
173
- const projectPackage = readJsonIfPresent(path.join(projectRoot, "package.json"));
174
- const dependency = dependencySpecForPackage(projectPackage, packageName);
175
- const lockfile = lockfileMetadataForProject(projectRoot);
176
- const lockfileInfo = lockfile.kind === "npm"
177
- ? npmLockfileInfoForPackage(lockfile.data, packageName)
178
- : {
179
- version: null,
180
- resolved: null,
181
- integrity: null,
182
- entryPath: null
183
- };
184
- const installedPackageJsonPath = path.join(projectRoot, "node_modules", ...packageName.split("/"), "package.json");
185
- const installedPackage = readJsonIfPresent(installedPackageJsonPath);
186
- return {
187
- dependencyField: dependency.field,
188
- dependencySpec: dependency.spec,
189
- installedVersion: typeof installedPackage?.version === "string" ? installedPackage.version : null,
190
- installedPackageJsonPath: installedPackage ? installedPackageJsonPath : null,
191
- lockfileKind: lockfile.kind,
192
- lockfilePath: lockfile.path,
193
- lockfileVersion: lockfileInfo.version,
194
- lockfileResolved: lockfileInfo.resolved,
195
- lockfileIntegrity: lockfileInfo.integrity,
196
- lockfileEntryPath: lockfileInfo.entryPath,
197
- lockfileNote: lockfile.note
198
- };
199
- }
200
-
201
- /**
202
- * @param {ReturnType<typeof packageInfoForGenerator>} packageInfo
203
- * @returns {string}
204
- */
205
- function formatGeneratorPackageLockfile(packageInfo) {
206
- if (!packageInfo.lockfileKind || !packageInfo.lockfilePath) {
207
- return "(not found)";
208
- }
209
- const label = path.basename(packageInfo.lockfilePath);
210
- if (packageInfo.lockfileVersion) {
211
- return `${packageInfo.lockfileKind} ${packageInfo.lockfileVersion}`;
212
- }
213
- return `${label} (version not inspected)`;
214
- }
215
-
216
- /**
217
- * @param {string} projectRoot
218
- * @param {any} policy
219
- * @param {ReturnType<typeof packageBackedGeneratorBindings>[number]} binding
220
- * @returns {ReturnType<typeof packageBackedGeneratorBindings>[number] & { allowed: boolean, packageInfo: ReturnType<typeof packageInfoForGenerator>, pin: { key: string|null, version: string|null, matches: boolean|null } }}
221
- */
222
- function generatorPolicyBindingStatus(projectRoot, policy, binding) {
223
- const packagePin = policy.pinnedVersions[binding.packageName] || null;
224
- const generatorPin = policy.pinnedVersions[binding.generatorId] || null;
225
- const pinnedVersion = packagePin || generatorPin;
226
- return {
227
- ...binding,
228
- allowed: generatorPackageAllowed(policy, binding.packageName),
229
- packageInfo: packageInfoForGenerator(projectRoot, binding.packageName),
230
- pin: {
231
- key: packagePin ? binding.packageName : generatorPin ? binding.generatorId : null,
232
- version: pinnedVersion,
233
- matches: pinnedVersion ? pinnedVersion === binding.version : null
234
- }
235
- };
236
- }
237
-
238
- /**
239
- * @param {any[]} diagnostics
240
- * @param {Array<ReturnType<typeof generatorPolicyBindingStatus>>} bindings
241
- * @returns {any[]}
242
- */
243
- function annotateGeneratorPolicyDiagnostics(diagnostics, bindings) {
244
- return diagnostics.map((diagnostic) => {
245
- const binding = bindings.find((item) => (
246
- item.packageName === diagnostic.packageName &&
247
- (!diagnostic.runtimeId || item.runtimeId === diagnostic.runtimeId)
248
- ));
249
- if (!binding) {
250
- return diagnostic;
251
- }
252
- return {
253
- ...diagnostic,
254
- packageVersion: binding.packageInfo.installedVersion || binding.packageInfo.lockfileVersion || null,
255
- packageDependencyField: binding.packageInfo.dependencyField,
256
- packageDependencySpec: binding.packageInfo.dependencySpec,
257
- packageLockfileKind: binding.packageInfo.lockfileKind,
258
- packageLockfilePath: binding.packageInfo.lockfilePath,
259
- packageLockVersion: binding.packageInfo.lockfileVersion
260
- };
261
- });
262
- }
263
-
264
- /**
265
- * @param {Array<ReturnType<typeof generatorPolicyBindingStatus>>} bindings
266
- * @returns {any[]}
267
- */
268
- function generatorPolicyPackageMetadataDiagnostics(bindings) {
269
- const diagnostics = [];
270
- for (const binding of bindings) {
271
- if (!binding.packageInfo.dependencySpec) {
272
- diagnostics.push({
273
- code: "generator_package_dependency_missing",
274
- severity: "warning",
275
- message: `Runtime '${binding.runtimeId}' generator package '${binding.packageName}' is not declared in package.json dependencies.`,
276
- path: binding.packageInfo.installedPackageJsonPath,
277
- suggestedFix: `Declare '${binding.packageName}' in package.json devDependencies so generator adoption is visible in package review.`,
278
- step: "generator-policy",
279
- runtimeId: binding.runtimeId,
280
- generatorId: binding.generatorId,
281
- packageName: binding.packageName,
282
- version: binding.version,
283
- packageVersion: binding.packageInfo.installedVersion || binding.packageInfo.lockfileVersion || null,
284
- packageDependencyField: binding.packageInfo.dependencyField,
285
- packageDependencySpec: binding.packageInfo.dependencySpec,
286
- packageLockfileKind: binding.packageInfo.lockfileKind,
287
- packageLockfilePath: binding.packageInfo.lockfilePath,
288
- packageLockVersion: binding.packageInfo.lockfileVersion
289
- });
290
- }
291
- if (
292
- binding.packageInfo.installedVersion &&
293
- binding.packageInfo.lockfileVersion &&
294
- binding.packageInfo.installedVersion !== binding.packageInfo.lockfileVersion
295
- ) {
296
- diagnostics.push({
297
- code: "generator_package_version_drift",
298
- severity: "warning",
299
- message: `Runtime '${binding.runtimeId}' generator package '${binding.packageName}' is installed at '${binding.packageInfo.installedVersion}', but package-lock records '${binding.packageInfo.lockfileVersion}'.`,
300
- path: binding.packageInfo.lockfilePath,
301
- suggestedFix: "Run the package manager install command and review the resulting lockfile before pinning generator policy.",
302
- step: "generator-policy",
303
- runtimeId: binding.runtimeId,
304
- generatorId: binding.generatorId,
305
- packageName: binding.packageName,
306
- version: binding.version,
307
- packageVersion: binding.packageInfo.installedVersion,
308
- packageDependencyField: binding.packageInfo.dependencyField,
309
- packageDependencySpec: binding.packageInfo.dependencySpec,
310
- packageLockfileKind: binding.packageInfo.lockfileKind,
311
- packageLockfilePath: binding.packageInfo.lockfilePath,
312
- packageLockVersion: binding.packageInfo.lockfileVersion
313
- });
314
- }
315
- }
316
- return diagnostics;
317
- }
318
-
319
- /**
320
- * @param {string} projectPath
321
- * @returns {{ ok: boolean, path: string, exists: boolean, policy: any, defaulted: boolean, bindings: Array<ReturnType<typeof generatorPolicyBindingStatus>>, diagnostics: any[], errors: string[] }}
322
- */
323
- export function buildGeneratorPolicyCheckPayload(projectPath) {
324
- const projectConfigInfo = loadProjectConfig(projectPath);
325
- if (!projectConfigInfo) {
326
- const diagnostic = {
327
- code: "generator_policy_project_missing",
328
- severity: "error",
329
- message: "Cannot check generator policy without topogram.project.json.",
330
- path: path.resolve(projectPath),
331
- suggestedFix: "Run this command in a Topogram project.",
332
- step: "generator-policy"
333
- };
334
- return {
335
- ok: false,
336
- path: path.join(path.resolve(projectPath), GENERATOR_POLICY_FILE),
337
- exists: false,
338
- policy: null,
339
- defaulted: false,
340
- bindings: [],
341
- diagnostics: [diagnostic],
342
- errors: [diagnostic.message]
343
- };
344
- }
345
- const policyInfo = loadGeneratorPolicy(projectConfigInfo.configDir);
346
- const rawBindings = packageBackedGeneratorBindings(projectConfigInfo.config);
347
- const policy = policyInfo.policy || effectiveGeneratorPolicy(policyInfo);
348
- const bindings = rawBindings.map((binding) => generatorPolicyBindingStatus(projectConfigInfo.configDir, policy, binding));
349
- const diagnostics = [];
350
- if (!policyInfo.exists) {
351
- diagnostics.push({
352
- code: "generator_policy_missing",
353
- severity: "warning",
354
- message: `No ${GENERATOR_POLICY_FILE} found. Default generator policy allows @topogram/* package-backed generators and blocks other package scopes.`,
355
- path: policyInfo.path,
356
- suggestedFix: "Run `topogram generator policy init` to write an explicit project generator policy after review.",
357
- step: "generator-policy"
358
- });
359
- }
360
- diagnostics.push(...generatorPolicyDiagnosticsForBindings(policyInfo, rawBindings, "generator-policy"));
361
- diagnostics.push(...generatorPolicyPackageMetadataDiagnostics(bindings));
362
- const annotatedDiagnostics = annotateGeneratorPolicyDiagnostics(diagnostics, bindings);
363
- const errors = annotatedDiagnostics.filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => diagnostic.message);
364
- return {
365
- ok: errors.length === 0,
366
- path: policyInfo.path,
367
- exists: policyInfo.exists,
368
- policy,
369
- defaulted: !policyInfo.exists,
370
- bindings,
371
- diagnostics: annotatedDiagnostics,
372
- errors
373
- };
374
- }
375
-
376
- /**
377
- * @param {string} projectPath
378
- * @returns {ReturnType<typeof buildGeneratorPolicyCheckPayload> & { rules: Array<{ name: string, ok: boolean, actual: string, expected: string, message: string, fix: string|null }> }}
379
- */
380
- export function buildGeneratorPolicyExplainPayload(projectPath) {
381
- const check = buildGeneratorPolicyCheckPayload(projectPath);
382
- const policy = check.policy || effectiveGeneratorPolicy({ path: check.path, exists: false, policy: null, diagnostics: [] });
383
- const rules = [];
384
- rules.push(generatorPolicyRule(
385
- "policy-file",
386
- check.exists,
387
- check.exists ? "present" : "missing",
388
- "present",
389
- check.exists
390
- ? "Project has a generator policy file."
391
- : "Project is using the default generator policy.",
392
- check.exists ? null : "Run `topogram generator policy init` after review."
393
- ));
394
- for (const binding of check.bindings) {
395
- const scope = packageScopeFromName(binding.packageName);
396
- rules.push(generatorPolicyRule(
397
- "allowed-package",
398
- generatorPackageAllowed(policy, binding.packageName),
399
- `${binding.packageName}${scope ? ` (${scope})` : ""}`,
400
- [
401
- `scopes=${policy.allowedPackageScopes.join(", ") || "(none)"}`,
402
- `packages=${policy.allowedPackages.join(", ") || "(none)"}`
403
- ].join("; "),
404
- `Runtime '${binding.runtimeId}' package-backed generator must be from an allowed package or scope.`,
405
- `Run \`topogram generator policy pin ${binding.packageName}@${binding.version}\` after reviewing the generator package.`
406
- ));
407
- const pinnedVersion = policy.pinnedVersions[binding.packageName] || policy.pinnedVersions[binding.generatorId] || null;
408
- rules.push(generatorPolicyRule(
409
- "pinned-version",
410
- !pinnedVersion || pinnedVersion === binding.version,
411
- binding.version,
412
- pinnedVersion || "(unpinned)",
413
- `Runtime '${binding.runtimeId}' generator version must match its policy pin when one exists.`,
414
- `Run \`topogram generator policy pin ${binding.packageName}@${binding.version}\` after review.`
415
- ));
416
- }
417
- return {
418
- ...check,
419
- rules
420
- };
421
- }
422
-
423
- /**
424
- * @param {string} projectPath
425
- * @returns {ReturnType<typeof buildGeneratorPolicyExplainPayload> & { summary: { packageBackedGenerators: number, allowed: number, denied: number, pinned: number, unpinned: number, pinMismatches: number } }}
426
- */
427
- export function buildGeneratorPolicyStatusPayload(projectPath) {
428
- const explain = buildGeneratorPolicyExplainPayload(projectPath);
429
- return {
430
- ...explain,
431
- summary: {
432
- packageBackedGenerators: explain.bindings.length,
433
- allowed: explain.bindings.filter((binding) => binding.allowed).length,
434
- denied: explain.bindings.filter((binding) => !binding.allowed).length,
435
- pinned: explain.bindings.filter((binding) => Boolean(binding.pin.version)).length,
436
- unpinned: explain.bindings.filter((binding) => !binding.pin.version).length,
437
- pinMismatches: explain.bindings.filter((binding) => binding.pin.matches === false).length
438
- }
439
- };
440
- }
441
-
442
- /**
443
- * @param {string} projectPath
444
- * @returns {{ ok: boolean, path: string, policy: any, diagnostics: any[], errors: string[] }}
445
- */
446
- export function buildGeneratorPolicyInitPayload(projectPath) {
447
- const projectConfigInfo = loadProjectConfig(projectPath);
448
- if (!projectConfigInfo) {
449
- throw new Error("Cannot initialize generator policy without topogram.project.json.");
450
- }
451
- const policy = writeGeneratorPolicy(projectConfigInfo.configDir, defaultGeneratorPolicy());
452
- return {
453
- ok: true,
454
- path: path.join(projectConfigInfo.configDir, GENERATOR_POLICY_FILE),
455
- policy,
456
- diagnostics: [],
457
- errors: []
458
- };
459
- }
460
-
461
- /**
462
- * @param {ReturnType<typeof buildGeneratorPolicyInitPayload>} payload
463
- * @returns {void}
464
- */
465
- export function printGeneratorPolicyInitPayload(payload) {
466
- console.log(`Wrote generator policy: ${payload.path}`);
467
- console.log(`Allowed package scopes: ${payload.policy.allowedPackageScopes.join(", ") || "(none)"}`);
468
- console.log(`Allowed packages: ${payload.policy.allowedPackages.join(", ") || "(none)"}`);
469
- }
470
-
471
- /**
472
- * @param {ReturnType<typeof buildGeneratorPolicyCheckPayload>} payload
473
- * @returns {void}
474
- */
475
- export function printGeneratorPolicyCheckPayload(payload) {
476
- console.log(payload.ok ? "Generator policy check passed" : "Generator policy check failed");
477
- console.log(`Policy: ${payload.path}`);
478
- console.log(`Exists: ${payload.exists ? "yes" : "no"}`);
479
- console.log(`Defaulted: ${payload.defaulted ? "yes" : "no"}`);
480
- console.log(`Package-backed generators: ${payload.bindings.length}`);
481
- for (const binding of payload.bindings) {
482
- console.log(`- ${binding.runtimeId}: ${binding.generatorId}@${binding.version} via ${binding.packageName}`);
483
- console.log(` npm package: ${binding.packageInfo.installedVersion || "(not installed)"}`);
484
- if (binding.packageInfo.dependencySpec) {
485
- console.log(` dependency: ${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}`);
486
- }
487
- if (binding.packageInfo.lockfileVersion) {
488
- console.log(` lockfile: ${formatGeneratorPackageLockfile(binding.packageInfo)}`);
489
- } else if (binding.packageInfo.lockfileKind) {
490
- console.log(` lockfile: ${formatGeneratorPackageLockfile(binding.packageInfo)}`);
491
- }
492
- }
493
- for (const diagnostic of payload.diagnostics) {
494
- console.log(`[${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`);
495
- if (diagnostic.path) {
496
- console.log(` path: ${diagnostic.path}`);
497
- }
498
- if (diagnostic.suggestedFix) {
499
- console.log(` fix: ${diagnostic.suggestedFix}`);
500
- }
501
- }
502
- }
503
-
504
- /**
505
- * @param {ReturnType<typeof buildGeneratorPolicyStatusPayload>} payload
506
- * @returns {void}
507
- */
508
- export function printGeneratorPolicyStatusPayload(payload) {
509
- console.log(payload.ok ? "Generator policy status: allowed" : "Generator policy status: denied");
510
- console.log(`Policy file: ${payload.path}`);
511
- console.log(`Policy file exists: ${payload.exists ? "yes" : "no"}`);
512
- console.log(`Default policy active: ${payload.defaulted ? "yes" : "no"}`);
513
- console.log(`Package-backed generators: ${payload.summary.packageBackedGenerators}`);
514
- console.log(`Allowed packages: ${payload.summary.allowed}`);
515
- console.log(`Denied packages: ${payload.summary.denied}`);
516
- console.log(`Pinned generators: ${payload.summary.pinned}`);
517
- console.log(`Unpinned generators: ${payload.summary.unpinned}`);
518
- console.log(`Pin mismatches: ${payload.summary.pinMismatches}`);
519
- if (payload.bindings.length > 0) {
520
- console.log("");
521
- console.log("Generator packages:");
522
- }
523
- for (const binding of payload.bindings) {
524
- console.log(`- ${binding.runtimeId}: ${binding.generatorId}@${binding.version} via ${binding.packageName}`);
525
- console.log(` allowed: ${binding.allowed ? "yes" : "no"}`);
526
- console.log(` npm package: ${binding.packageInfo.installedVersion || "(not installed)"}`);
527
- console.log(` dependency: ${binding.packageInfo.dependencyField && binding.packageInfo.dependencySpec ? `${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}` : "(not declared)"}`);
528
- console.log(` lockfile: ${formatGeneratorPackageLockfile(binding.packageInfo)}`);
529
- console.log(` policy pin: ${binding.pin.version ? `${binding.pin.key}@${binding.pin.version}` : "(none)"}`);
530
- }
531
- for (const diagnostic of payload.diagnostics) {
532
- const label = diagnostic.severity === "warning" ? "Warning" : "Error";
533
- console.log(`${label}: ${diagnostic.code}: ${diagnostic.message}`);
534
- if (diagnostic.packageVersion) {
535
- console.log(` package version: ${diagnostic.packageVersion}`);
536
- }
537
- if (diagnostic.packageDependencySpec) {
538
- console.log(` dependency: ${diagnostic.packageDependencyField} ${diagnostic.packageDependencySpec}`);
539
- }
540
- if (diagnostic.packageLockfilePath) {
541
- console.log(` lockfile: ${path.basename(diagnostic.packageLockfilePath)}${diagnostic.packageLockVersion ? ` ${diagnostic.packageLockVersion}` : ""}`);
542
- }
543
- if (diagnostic.suggestedFix) {
544
- console.log(` fix: ${diagnostic.suggestedFix}`);
545
- }
546
- }
547
- }
548
-
549
- /**
550
- * @param {ReturnType<typeof buildGeneratorPolicyExplainPayload>} payload
551
- * @returns {void}
552
- */
553
- export function printGeneratorPolicyExplainPayload(payload) {
554
- console.log(payload.ok ? "Generator policy: allowed" : "Generator policy: denied");
555
- console.log(payload.ok
556
- ? "Decision: package-backed generators are allowed by this project's generator policy."
557
- : "Decision: one or more package-backed generators are blocked by this project's generator policy.");
558
- console.log(`Policy file: ${payload.path}`);
559
- console.log(`Policy file exists: ${payload.exists ? "yes" : "no"}`);
560
- console.log(`Default policy active: ${payload.defaulted ? "yes" : "no"}`);
561
- if (payload.bindings.length > 0) {
562
- console.log("");
563
- console.log("Package-backed generators:");
564
- for (const binding of payload.bindings) {
565
- console.log(`- ${binding.runtimeId}: ${binding.generatorId}@${binding.version} via ${binding.packageName}`);
566
- console.log(` npm package: ${binding.packageInfo.installedVersion || "(not installed)"}`);
567
- if (binding.packageInfo.dependencySpec) {
568
- console.log(` dependency: ${binding.packageInfo.dependencyField} ${binding.packageInfo.dependencySpec}`);
569
- }
570
- }
571
- }
572
- if (payload.rules.length > 0) {
573
- console.log("");
574
- console.log("Policy checks:");
575
- }
576
- for (const rule of payload.rules) {
577
- console.log(`${rule.ok ? "PASS" : "FAIL"} ${generatorPolicyRuleLabel(rule.name)}: ${rule.message}`);
578
- console.log(` actual: ${rule.actual}`);
579
- console.log(` expected: ${rule.expected}`);
580
- if (!rule.ok && rule.fix) {
581
- console.log(` fix: ${rule.fix}`);
582
- }
583
- }
584
- for (const diagnostic of payload.diagnostics) {
585
- const label = diagnostic.severity === "warning" ? "Warning" : "Error";
586
- console.log(`${label}: ${diagnostic.code}: ${diagnostic.message}`);
587
- if (diagnostic.suggestedFix) {
588
- console.log(` fix: ${diagnostic.suggestedFix}`);
589
- }
590
- }
591
- }
592
-
593
- /**
594
- * @param {string} projectPath
595
- * @param {string|null|undefined} spec
596
- * @returns {{ ok: boolean, path: string, policy: any, pinned: Array<{ packageName: string, version: string }>, diagnostics: any[], errors: string[] }}
597
- */
598
- export function buildGeneratorPolicyPinPayload(projectPath, spec) {
599
- const projectConfigInfo = loadProjectConfig(projectPath);
600
- if (!projectConfigInfo) {
601
- const diagnostic = {
602
- code: "generator_policy_project_missing",
603
- severity: "error",
604
- message: "Cannot pin generator policy without topogram.project.json.",
605
- path: path.resolve(projectPath),
606
- suggestedFix: "Run this command in a Topogram project.",
607
- step: "generator-policy"
608
- };
609
- return {
610
- ok: false,
611
- path: path.join(path.resolve(projectPath), GENERATOR_POLICY_FILE),
612
- policy: null,
613
- pinned: [],
614
- diagnostics: [diagnostic],
615
- errors: [diagnostic.message]
616
- };
617
- }
618
- const policyInfo = loadGeneratorPolicy(projectConfigInfo.configDir);
619
- const policyDiagnostics = /** @type {any[]} */ (policyInfo.diagnostics || []);
620
- if (policyDiagnostics.some((diagnostic) => diagnostic.severity === "error")) {
621
- const errors = policyDiagnostics.filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => diagnostic.message);
622
- return {
623
- ok: false,
624
- path: policyInfo.path,
625
- policy: policyInfo.policy,
626
- pinned: [],
627
- diagnostics: policyDiagnostics,
628
- errors
629
- };
630
- }
631
- let pins = [];
632
- try {
633
- pins = spec
634
- ? [parseGeneratorPolicyPin(spec)]
635
- : packageBackedGeneratorBindings(projectConfigInfo.config).map((binding) => ({
636
- packageName: binding.packageName,
637
- version: binding.version
638
- }));
639
- } catch (error) {
640
- const diagnostic = {
641
- code: "generator_policy_pin_invalid",
642
- severity: "error",
643
- message: error instanceof Error ? error.message : String(error),
644
- path: policyInfo.path,
645
- suggestedFix: "Pass a pin such as @topogram/generator-react-web@1.",
646
- step: "generator-policy"
647
- };
648
- return {
649
- ok: false,
650
- path: policyInfo.path,
651
- policy: policyInfo.policy,
652
- pinned: [],
653
- diagnostics: [diagnostic],
654
- errors: [diagnostic.message]
655
- };
656
- }
657
- if (pins.length === 0) {
658
- const diagnostic = {
659
- code: "generator_policy_pin_no_generators",
660
- severity: "error",
661
- message: "No package-backed topology generator bindings are available to pin.",
662
- path: projectConfigInfo.configPath,
663
- suggestedFix: "Pass an explicit pin such as @topogram/generator-react-web@1, or use bundled generators.",
664
- step: "generator-policy"
665
- };
666
- return {
667
- ok: false,
668
- path: policyInfo.path,
669
- policy: policyInfo.policy,
670
- pinned: [],
671
- diagnostics: [diagnostic],
672
- errors: [diagnostic.message]
673
- };
674
- }
675
- const policy = policyInfo.policy || defaultGeneratorPolicy();
676
- const allowedPackages = [...policy.allowedPackages];
677
- const allowedPackageScopes = [...policy.allowedPackageScopes];
678
- const pinnedVersions = { ...policy.pinnedVersions };
679
- for (const pin of pins) {
680
- if (!allowedPackages.includes(pin.packageName)) {
681
- allowedPackages.push(pin.packageName);
682
- }
683
- pinnedVersions[pin.packageName] = pin.version;
684
- }
685
- const nextPolicy = {
686
- ...policy,
687
- allowedPackageScopes,
688
- allowedPackages,
689
- pinnedVersions
690
- };
691
- writeGeneratorPolicy(projectConfigInfo.configDir, nextPolicy);
692
- return {
693
- ok: true,
694
- path: path.join(projectConfigInfo.configDir, GENERATOR_POLICY_FILE),
695
- policy: nextPolicy,
696
- pinned: pins,
697
- diagnostics: [],
698
- errors: []
699
- };
700
- }
701
-
702
- /**
703
- * @param {{ ok: boolean, path: string, pinned: Array<{ packageName: string, version: string }>, diagnostics: any[] }} payload
704
- * @returns {void}
705
- */
706
- export function printGeneratorPolicyPinPayload(payload) {
707
- console.log(payload.ok ? "Generator policy pin updated" : "Generator policy pin failed");
708
- console.log(`Policy: ${payload.path}`);
709
- for (const pin of payload.pinned) {
710
- console.log(`Pinned: ${pin.packageName}@${pin.version}`);
711
- }
712
- for (const diagnostic of payload.diagnostics) {
713
- console.log(`[${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`);
714
- if (diagnostic.path) {
715
- console.log(` path: ${diagnostic.path}`);
716
- }
717
- if (diagnostic.suggestedFix) {
718
- console.log(` fix: ${diagnostic.suggestedFix}`);
719
- }
720
- }
721
- }
722
-
723
- /**
724
- * @param {{
725
- * commandArgs: Record<string, any>,
726
- * inputPath: string|null|undefined,
727
- * json: boolean
728
- * }} context
729
- * @returns {number}
730
- */
731
- export function runGeneratorPolicyCommand(context) {
732
- const { commandArgs, inputPath, json } = context;
733
- const projectPath = inputPath || "./topogram";
734
- if (commandArgs.generatorPolicyCommand === "init") {
735
- const payload = buildGeneratorPolicyInitPayload(projectPath);
736
- if (json) {
737
- console.log(stableStringify(payload));
738
- } else {
739
- printGeneratorPolicyInitPayload(payload);
740
- }
741
- return 0;
742
- }
743
-
744
- if (commandArgs.generatorPolicyCommand === "status") {
745
- const payload = buildGeneratorPolicyStatusPayload(projectPath);
746
- if (json) {
747
- console.log(stableStringify(payload));
748
- } else {
749
- printGeneratorPolicyStatusPayload(payload);
750
- }
751
- return payload.ok ? 0 : 1;
752
- }
753
-
754
- if (commandArgs.generatorPolicyCommand === "check") {
755
- const payload = buildGeneratorPolicyCheckPayload(projectPath);
756
- if (json) {
757
- console.log(stableStringify(payload));
758
- } else {
759
- printGeneratorPolicyCheckPayload(payload);
760
- }
761
- return payload.ok ? 0 : 1;
762
- }
763
-
764
- if (commandArgs.generatorPolicyCommand === "explain") {
765
- const payload = buildGeneratorPolicyExplainPayload(projectPath);
766
- if (json) {
767
- console.log(stableStringify(payload));
768
- } else {
769
- printGeneratorPolicyExplainPayload(payload);
770
- }
771
- return payload.ok ? 0 : 1;
772
- }
773
-
774
- if (commandArgs.generatorPolicyCommand === "pin") {
775
- const payload = buildGeneratorPolicyPinPayload(projectPath, commandArgs.generatorPolicyPinSpec);
776
- if (json) {
777
- console.log(stableStringify(payload));
778
- } else {
779
- printGeneratorPolicyPinPayload(payload);
780
- }
781
- return payload.ok ? 0 : 1;
782
- }
783
-
784
- throw new Error(`Unknown generator policy command '${commandArgs.generatorPolicyCommand}'`);
785
- }
3
+ export {
4
+ buildGeneratorPolicyCheckPayload,
5
+ buildGeneratorPolicyExplainPayload,
6
+ buildGeneratorPolicyInitPayload,
7
+ buildGeneratorPolicyPinPayload,
8
+ buildGeneratorPolicyStatusPayload
9
+ } from "./generator-policy/payloads.js";
10
+ export {
11
+ printGeneratorPolicyCheckPayload,
12
+ printGeneratorPolicyExplainPayload,
13
+ printGeneratorPolicyInitPayload,
14
+ printGeneratorPolicyPinPayload,
15
+ printGeneratorPolicyStatusPayload
16
+ } from "./generator-policy/printers.js";
17
+ export { runGeneratorPolicyCommand } from "./generator-policy/runner.js";