@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
@@ -0,0 +1,252 @@
1
+ // @ts-check
2
+
3
+ import { CATALOG_FILE_NAME, KNOWN_CATALOG_SURFACES } from "./constants.js";
4
+ import { catalogDiagnostic } from "./diagnostics.js";
5
+
6
+ /**
7
+ * @param {unknown} value
8
+ * @param {string} source
9
+ * @returns {{ ok: boolean, catalog: any|null, diagnostics: any[], errors: string[] }}
10
+ */
11
+ export function validateCatalog(value, source = "") {
12
+ /** @type {any[]} */
13
+ const diagnostics = [];
14
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
15
+ diagnostics.push(catalogDiagnostic({
16
+ code: "catalog_not_object",
17
+ message: "Catalog must contain a JSON object.",
18
+ path: source || null,
19
+ suggestedFix: `Create ${CATALOG_FILE_NAME} with version and entries[].`
20
+ }));
21
+ return validationResult(null, diagnostics);
22
+ }
23
+
24
+ const input = /** @type {Record<string, unknown>} */ (value);
25
+ const version = typeof input.version === "string" && input.version ? input.version : "";
26
+ if (!version) {
27
+ diagnostics.push(catalogDiagnostic({
28
+ code: "catalog_version_missing",
29
+ message: "Catalog is missing required string field 'version'.",
30
+ path: source || null,
31
+ suggestedFix: "Add a version string such as \"0.1\"."
32
+ }));
33
+ }
34
+ if (!Array.isArray(input.entries)) {
35
+ diagnostics.push(catalogDiagnostic({
36
+ code: "catalog_entries_missing",
37
+ message: "Catalog is missing required array field 'entries'.",
38
+ path: source || null,
39
+ suggestedFix: "Add entries[] with template and topogram package references."
40
+ }));
41
+ return validationResult({ version, entries: [] }, diagnostics);
42
+ }
43
+
44
+ /** @type {any[]} */
45
+ const entries = [];
46
+ const ids = new Set();
47
+ input.entries.forEach((entryValue, index) => {
48
+ const entryPath = `entries[${index}]`;
49
+ if (!entryValue || typeof entryValue !== "object" || Array.isArray(entryValue)) {
50
+ diagnostics.push(catalogDiagnostic({
51
+ code: "catalog_entry_not_object",
52
+ message: `Catalog ${entryPath} must be an object.`,
53
+ path: source || null,
54
+ suggestedFix: "Replace the entry with an object containing id, kind, package, defaultVersion, description, tags, and trust."
55
+ }));
56
+ return;
57
+ }
58
+ const entry = /** @type {Record<string, unknown>} */ (entryValue);
59
+ const id = stringField(entry, "id");
60
+ const kind = stringField(entry, "kind");
61
+ const packageName = stringField(entry, "package");
62
+ const defaultVersion = stringField(entry, "defaultVersion");
63
+ const description = stringField(entry, "description");
64
+ const tags = Array.isArray(entry.tags) ? entry.tags.map(String).filter(Boolean) : [];
65
+ const surfaces = Array.isArray(entry.surfaces) ? entry.surfaces.map(String).filter(Boolean) : [];
66
+ const generators = Array.isArray(entry.generators) ? entry.generators.map(String).filter(Boolean) : [];
67
+ const stack = stringField(entry, "stack");
68
+ const trust = trustField(entry.trust);
69
+
70
+ for (const field of ["id", "kind", "package", "defaultVersion", "description"]) {
71
+ if (!stringField(entry, field)) {
72
+ diagnostics.push(catalogDiagnostic({
73
+ code: "catalog_entry_field_missing",
74
+ message: `Catalog ${entryPath} is missing required string field '${field}'.`,
75
+ path: source || null,
76
+ suggestedFix: `Add ${field} to ${entryPath}.`
77
+ }));
78
+ }
79
+ }
80
+ if (id && ids.has(id)) {
81
+ diagnostics.push(catalogDiagnostic({
82
+ code: "catalog_duplicate_id",
83
+ message: `Catalog entry id '${id}' is duplicated.`,
84
+ path: source || null,
85
+ suggestedFix: "Use stable unique ids for catalog entries."
86
+ }));
87
+ }
88
+ if (id) {
89
+ ids.add(id);
90
+ }
91
+ if (kind && kind !== "template" && kind !== "topogram") {
92
+ diagnostics.push(catalogDiagnostic({
93
+ code: "catalog_invalid_kind",
94
+ message: `Catalog entry '${id || entryPath}' has invalid kind '${kind}'.`,
95
+ path: source || null,
96
+ suggestedFix: "Use kind \"template\" or \"topogram\"."
97
+ }));
98
+ }
99
+ if (packageName && !isPackageName(packageName)) {
100
+ diagnostics.push(catalogDiagnostic({
101
+ code: "catalog_invalid_package",
102
+ message: `Catalog entry '${id || entryPath}' package must be an npm package name, not '${packageName}'.`,
103
+ path: source || null,
104
+ suggestedFix: "Use package plus defaultVersion separately, for example @scope/topogram-template-name and 0.1.0."
105
+ }));
106
+ }
107
+ if (defaultVersion && /\s/.test(defaultVersion)) {
108
+ diagnostics.push(catalogDiagnostic({
109
+ code: "catalog_invalid_default_version",
110
+ message: `Catalog entry '${id || entryPath}' defaultVersion must not contain whitespace.`,
111
+ path: source || null,
112
+ suggestedFix: "Use an exact version or npm dist-tag."
113
+ }));
114
+ }
115
+ if (!Array.isArray(entry.tags)) {
116
+ diagnostics.push(catalogDiagnostic({
117
+ code: "catalog_tags_missing",
118
+ message: `Catalog entry '${id || entryPath}' is missing required tags array.`,
119
+ path: source || null,
120
+ suggestedFix: "Add tags as an array of strings."
121
+ }));
122
+ }
123
+ if (Object.prototype.hasOwnProperty.call(entry, "surfaces") && !Array.isArray(entry.surfaces)) {
124
+ diagnostics.push(catalogDiagnostic({
125
+ code: "catalog_optional_surfaces_invalid",
126
+ severity: "warning",
127
+ message: `Catalog entry '${id || entryPath}' surfaces should be an array of surface ids.`,
128
+ path: source || null,
129
+ suggestedFix: "Use surfaces such as [\"web\"], [\"api\"], [\"database\"], or [\"native\"]."
130
+ }));
131
+ }
132
+ for (const surface of surfaces) {
133
+ if (!KNOWN_CATALOG_SURFACES.has(surface)) {
134
+ diagnostics.push(catalogDiagnostic({
135
+ code: "catalog_optional_surface_unknown",
136
+ severity: "warning",
137
+ message: `Catalog entry '${id || entryPath}' has unknown surface '${surface}'.`,
138
+ path: source || null,
139
+ suggestedFix: "Use known surface ids: web, api, database, native."
140
+ }));
141
+ }
142
+ }
143
+ if (Object.prototype.hasOwnProperty.call(entry, "generators") && !Array.isArray(entry.generators)) {
144
+ diagnostics.push(catalogDiagnostic({
145
+ code: "catalog_optional_generators_invalid",
146
+ severity: "warning",
147
+ message: `Catalog entry '${id || entryPath}' generators should be an array of generator ids.`,
148
+ path: source || null,
149
+ suggestedFix: "Use package-backed generator ids such as [\"@topogram/generator-sveltekit-web\", \"@topogram/generator-hono-api\"]."
150
+ }));
151
+ }
152
+ if (Object.prototype.hasOwnProperty.call(entry, "stack") && typeof entry.stack !== "string") {
153
+ diagnostics.push(catalogDiagnostic({
154
+ code: "catalog_optional_stack_invalid",
155
+ severity: "warning",
156
+ message: `Catalog entry '${id || entryPath}' stack should be a string.`,
157
+ path: source || null,
158
+ suggestedFix: "Use a short stack label such as \"SvelteKit + Hono + Postgres\"."
159
+ }));
160
+ }
161
+ if (!trust) {
162
+ diagnostics.push(catalogDiagnostic({
163
+ code: "catalog_trust_missing",
164
+ message: `Catalog entry '${id || entryPath}' is missing required trust metadata.`,
165
+ path: source || null,
166
+ suggestedFix: "Add trust.scope and trust.includesExecutableImplementation."
167
+ }));
168
+ } else if (kind === "topogram" && trust.includesExecutableImplementation) {
169
+ diagnostics.push(catalogDiagnostic({
170
+ code: "catalog_topogram_executable_not_supported",
171
+ message: `Catalog topogram entry '${id || entryPath}' cannot include executable implementation in v1.`,
172
+ path: source || null,
173
+ suggestedFix: "Move executable code into a template package, or set includesExecutableImplementation to false."
174
+ }));
175
+ }
176
+
177
+ entries.push({
178
+ id,
179
+ kind: kind === "topogram" ? "topogram" : "template",
180
+ package: packageName,
181
+ defaultVersion,
182
+ description,
183
+ tags,
184
+ ...(surfaces.length > 0 ? { surfaces } : {}),
185
+ ...(generators.length > 0 ? { generators } : {}),
186
+ ...(stack ? { stack } : {}),
187
+ trust: trust || { scope: "", includesExecutableImplementation: false }
188
+ });
189
+ });
190
+
191
+ return validationResult({ version, entries }, diagnostics);
192
+ }
193
+
194
+ /**
195
+ * @param {any|null} catalog
196
+ * @param {any[]} diagnostics
197
+ * @returns {{ ok: boolean, catalog: any|null, diagnostics: any[], errors: string[] }}
198
+ */
199
+ function validationResult(catalog, diagnostics) {
200
+ const errors = diagnostics
201
+ .filter((diagnostic) => diagnostic.severity === "error")
202
+ .map((diagnostic) => diagnostic.message);
203
+ return {
204
+ ok: errors.length === 0,
205
+ catalog: errors.length === 0 ? catalog : null,
206
+ diagnostics,
207
+ errors
208
+ };
209
+ }
210
+
211
+ /**
212
+ * @param {Record<string, unknown>} input
213
+ * @param {string} field
214
+ * @returns {string}
215
+ */
216
+ function stringField(input, field) {
217
+ const value = input[field];
218
+ return typeof value === "string" ? value.trim() : "";
219
+ }
220
+
221
+ /**
222
+ * @param {unknown} value
223
+ * @returns {{ scope: string, includesExecutableImplementation: boolean, notes?: string }|null}
224
+ */
225
+ function trustField(value) {
226
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
227
+ return null;
228
+ }
229
+ const trust = /** @type {Record<string, unknown>} */ (value);
230
+ if (typeof trust.scope !== "string" || !trust.scope) {
231
+ return null;
232
+ }
233
+ if (typeof trust.includesExecutableImplementation !== "boolean") {
234
+ return null;
235
+ }
236
+ const result = {
237
+ scope: trust.scope,
238
+ includesExecutableImplementation: trust.includesExecutableImplementation
239
+ };
240
+ if (typeof trust.notes === "string" && trust.notes) {
241
+ return { ...result, notes: trust.notes };
242
+ }
243
+ return result;
244
+ }
245
+
246
+ /**
247
+ * @param {string} value
248
+ * @returns {boolean}
249
+ */
250
+ function isPackageName(value) {
251
+ return /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i.test(value);
252
+ }
package/src/catalog.d.ts CHANGED
@@ -7,4 +7,6 @@ export function copyCatalogTopogramEntry(entry: any, targetPath: string, options
7
7
  export function findCatalogEntry(catalog: any, id: string, kind?: "template" | "topogram" | null): any | null;
8
8
  export function isCatalogSourceDisabled(source: string | null | undefined): boolean;
9
9
  export function loadCatalog(sourceInput?: string | null): any;
10
+ export function validateCatalog(value: unknown, source?: string): any;
11
+ export const CATALOG_FILE_NAME: string;
10
12
  export const TOPOGRAM_SOURCE_FILE: string;