@fuzdev/fuz_gitops 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -0
  3. package/dist/ModulesDetail.svelte +180 -0
  4. package/dist/ModulesDetail.svelte.d.ts +10 -0
  5. package/dist/ModulesDetail.svelte.d.ts.map +1 -0
  6. package/dist/ModulesNav.svelte +43 -0
  7. package/dist/ModulesNav.svelte.d.ts +11 -0
  8. package/dist/ModulesNav.svelte.d.ts.map +1 -0
  9. package/dist/ModulesPage.svelte +50 -0
  10. package/dist/ModulesPage.svelte.d.ts +9 -0
  11. package/dist/ModulesPage.svelte.d.ts.map +1 -0
  12. package/dist/PageFooter.svelte +15 -0
  13. package/dist/PageFooter.svelte.d.ts +19 -0
  14. package/dist/PageFooter.svelte.d.ts.map +1 -0
  15. package/dist/PageHeader.svelte +35 -0
  16. package/dist/PageHeader.svelte.d.ts +19 -0
  17. package/dist/PageHeader.svelte.d.ts.map +1 -0
  18. package/dist/PullRequestsDetail.svelte +53 -0
  19. package/dist/PullRequestsDetail.svelte.d.ts +10 -0
  20. package/dist/PullRequestsDetail.svelte.d.ts.map +1 -0
  21. package/dist/PullRequestsPage.svelte +47 -0
  22. package/dist/PullRequestsPage.svelte.d.ts +11 -0
  23. package/dist/PullRequestsPage.svelte.d.ts.map +1 -0
  24. package/dist/ReposTable.svelte +189 -0
  25. package/dist/ReposTable.svelte.d.ts +9 -0
  26. package/dist/ReposTable.svelte.d.ts.map +1 -0
  27. package/dist/ReposTree.svelte +88 -0
  28. package/dist/ReposTree.svelte.d.ts +11 -0
  29. package/dist/ReposTree.svelte.d.ts.map +1 -0
  30. package/dist/ReposTreeNav.svelte +55 -0
  31. package/dist/ReposTreeNav.svelte.d.ts +11 -0
  32. package/dist/ReposTreeNav.svelte.d.ts.map +1 -0
  33. package/dist/TablePage.svelte +46 -0
  34. package/dist/TablePage.svelte.d.ts +9 -0
  35. package/dist/TablePage.svelte.d.ts.map +1 -0
  36. package/dist/TreeItemPage.svelte +75 -0
  37. package/dist/TreeItemPage.svelte.d.ts +10 -0
  38. package/dist/TreeItemPage.svelte.d.ts.map +1 -0
  39. package/dist/TreePage.svelte +64 -0
  40. package/dist/TreePage.svelte.d.ts +9 -0
  41. package/dist/TreePage.svelte.d.ts.map +1 -0
  42. package/dist/changeset_generator.d.ts +38 -0
  43. package/dist/changeset_generator.d.ts.map +1 -0
  44. package/dist/changeset_generator.js +110 -0
  45. package/dist/changeset_reader.d.ts +75 -0
  46. package/dist/changeset_reader.d.ts.map +1 -0
  47. package/dist/changeset_reader.js +167 -0
  48. package/dist/constants.d.ts +9 -0
  49. package/dist/constants.d.ts.map +1 -0
  50. package/dist/constants.js +8 -0
  51. package/dist/dependency_graph.d.ts +120 -0
  52. package/dist/dependency_graph.d.ts.map +1 -0
  53. package/dist/dependency_graph.js +341 -0
  54. package/dist/dependency_updater.d.ts +46 -0
  55. package/dist/dependency_updater.d.ts.map +1 -0
  56. package/dist/dependency_updater.js +213 -0
  57. package/dist/fetch_repo_data.d.ts +19 -0
  58. package/dist/fetch_repo_data.d.ts.map +1 -0
  59. package/dist/fetch_repo_data.js +49 -0
  60. package/dist/fs_fetch_value_cache.d.ts +24 -0
  61. package/dist/fs_fetch_value_cache.d.ts.map +1 -0
  62. package/dist/fs_fetch_value_cache.js +61 -0
  63. package/dist/git_operations.d.ts +54 -0
  64. package/dist/git_operations.d.ts.map +1 -0
  65. package/dist/git_operations.js +144 -0
  66. package/dist/github.d.ts +91 -0
  67. package/dist/github.d.ts.map +1 -0
  68. package/dist/github.js +94 -0
  69. package/dist/github_helpers.d.ts +10 -0
  70. package/dist/github_helpers.d.ts.map +1 -0
  71. package/dist/github_helpers.js +13 -0
  72. package/dist/gitops_analyze.task.d.ts +17 -0
  73. package/dist/gitops_analyze.task.d.ts.map +1 -0
  74. package/dist/gitops_analyze.task.js +188 -0
  75. package/dist/gitops_config.d.ts +56 -0
  76. package/dist/gitops_config.d.ts.map +1 -0
  77. package/dist/gitops_config.js +63 -0
  78. package/dist/gitops_plan.task.d.ts +28 -0
  79. package/dist/gitops_plan.task.d.ts.map +1 -0
  80. package/dist/gitops_plan.task.js +217 -0
  81. package/dist/gitops_publish.task.d.ts +29 -0
  82. package/dist/gitops_publish.task.d.ts.map +1 -0
  83. package/dist/gitops_publish.task.js +178 -0
  84. package/dist/gitops_sync.task.d.ts +18 -0
  85. package/dist/gitops_sync.task.d.ts.map +1 -0
  86. package/dist/gitops_sync.task.js +95 -0
  87. package/dist/gitops_task_helpers.d.ts +63 -0
  88. package/dist/gitops_task_helpers.d.ts.map +1 -0
  89. package/dist/gitops_task_helpers.js +84 -0
  90. package/dist/gitops_validate.task.d.ts +12 -0
  91. package/dist/gitops_validate.task.d.ts.map +1 -0
  92. package/dist/gitops_validate.task.js +210 -0
  93. package/dist/graph_validation.d.ts +39 -0
  94. package/dist/graph_validation.d.ts.map +1 -0
  95. package/dist/graph_validation.js +79 -0
  96. package/dist/local_repo.d.ts +84 -0
  97. package/dist/local_repo.d.ts.map +1 -0
  98. package/dist/local_repo.js +213 -0
  99. package/dist/log_helpers.d.ts +43 -0
  100. package/dist/log_helpers.d.ts.map +1 -0
  101. package/dist/log_helpers.js +98 -0
  102. package/dist/multi_repo_publisher.d.ts +34 -0
  103. package/dist/multi_repo_publisher.d.ts.map +1 -0
  104. package/dist/multi_repo_publisher.js +364 -0
  105. package/dist/npm_install_helpers.d.ts +23 -0
  106. package/dist/npm_install_helpers.d.ts.map +1 -0
  107. package/dist/npm_install_helpers.js +60 -0
  108. package/dist/npm_registry.d.ts +46 -0
  109. package/dist/npm_registry.d.ts.map +1 -0
  110. package/dist/npm_registry.js +96 -0
  111. package/dist/operations.d.ts +409 -0
  112. package/dist/operations.d.ts.map +1 -0
  113. package/dist/operations.js +34 -0
  114. package/dist/operations_defaults.d.ts +19 -0
  115. package/dist/operations_defaults.d.ts.map +1 -0
  116. package/dist/operations_defaults.js +279 -0
  117. package/dist/output_helpers.d.ts +27 -0
  118. package/dist/output_helpers.d.ts.map +1 -0
  119. package/dist/output_helpers.js +39 -0
  120. package/dist/paths.d.ts +11 -0
  121. package/dist/paths.d.ts.map +1 -0
  122. package/dist/paths.js +10 -0
  123. package/dist/preflight_checks.d.ts +47 -0
  124. package/dist/preflight_checks.d.ts.map +1 -0
  125. package/dist/preflight_checks.js +181 -0
  126. package/dist/publishing_plan.d.ts +100 -0
  127. package/dist/publishing_plan.d.ts.map +1 -0
  128. package/dist/publishing_plan.js +353 -0
  129. package/dist/publishing_plan_helpers.d.ts +30 -0
  130. package/dist/publishing_plan_helpers.d.ts.map +1 -0
  131. package/dist/publishing_plan_helpers.js +112 -0
  132. package/dist/publishing_plan_logging.d.ts +18 -0
  133. package/dist/publishing_plan_logging.d.ts.map +1 -0
  134. package/dist/publishing_plan_logging.js +342 -0
  135. package/dist/repo.svelte.d.ts +52 -0
  136. package/dist/repo.svelte.d.ts.map +1 -0
  137. package/dist/repo.svelte.js +70 -0
  138. package/dist/repo_ops.d.ts +57 -0
  139. package/dist/repo_ops.d.ts.map +1 -0
  140. package/dist/repo_ops.js +167 -0
  141. package/dist/resolved_gitops_config.d.ts +9 -0
  142. package/dist/resolved_gitops_config.d.ts.map +1 -0
  143. package/dist/resolved_gitops_config.js +12 -0
  144. package/dist/semver.d.ts +24 -0
  145. package/dist/semver.d.ts.map +1 -0
  146. package/dist/semver.js +140 -0
  147. package/dist/serialization_types.d.ts +57 -0
  148. package/dist/serialization_types.d.ts.map +1 -0
  149. package/dist/serialization_types.js +40 -0
  150. package/dist/version_utils.d.ts +48 -0
  151. package/dist/version_utils.d.ts.map +1 -0
  152. package/dist/version_utils.js +125 -0
  153. package/package.json +107 -0
  154. package/src/lib/changeset_generator.ts +162 -0
  155. package/src/lib/changeset_reader.ts +218 -0
  156. package/src/lib/constants.ts +8 -0
  157. package/src/lib/dependency_graph.ts +423 -0
  158. package/src/lib/dependency_updater.ts +297 -0
  159. package/src/lib/fetch_repo_data.ts +64 -0
  160. package/src/lib/fs_fetch_value_cache.ts +75 -0
  161. package/src/lib/git_operations.ts +208 -0
  162. package/src/lib/github.ts +128 -0
  163. package/src/lib/github_helpers.ts +31 -0
  164. package/src/lib/gitops_analyze.task.ts +261 -0
  165. package/src/lib/gitops_config.ts +123 -0
  166. package/src/lib/gitops_plan.task.ts +272 -0
  167. package/src/lib/gitops_publish.task.ts +227 -0
  168. package/src/lib/gitops_sync.task.ts +109 -0
  169. package/src/lib/gitops_task_helpers.ts +126 -0
  170. package/src/lib/gitops_validate.task.ts +248 -0
  171. package/src/lib/graph_validation.ts +109 -0
  172. package/src/lib/local_repo.ts +359 -0
  173. package/src/lib/log_helpers.ts +147 -0
  174. package/src/lib/multi_repo_publisher.ts +464 -0
  175. package/src/lib/npm_install_helpers.ts +85 -0
  176. package/src/lib/npm_registry.ts +143 -0
  177. package/src/lib/operations.ts +334 -0
  178. package/src/lib/operations_defaults.ts +335 -0
  179. package/src/lib/output_helpers.ts +64 -0
  180. package/src/lib/paths.ts +11 -0
  181. package/src/lib/preflight_checks.ts +269 -0
  182. package/src/lib/publishing_plan.ts +531 -0
  183. package/src/lib/publishing_plan_helpers.ts +145 -0
  184. package/src/lib/publishing_plan_logging.ts +470 -0
  185. package/src/lib/repo.svelte.ts +95 -0
  186. package/src/lib/repo_ops.ts +213 -0
  187. package/src/lib/resolved_gitops_config.ts +27 -0
  188. package/src/lib/semver.ts +166 -0
  189. package/src/lib/serialization_types.ts +90 -0
  190. package/src/lib/version_utils.ts +150 -0
@@ -0,0 +1,470 @@
1
+ /**
2
+ * Logging and formatting functions for publishing plans.
3
+ *
4
+ * Includes both regular plan output and verbose diagnostic sections.
5
+ */
6
+
7
+ import type {Logger} from '@fuzdev/fuz_util/log.js';
8
+ import {styleText as st} from 'node:util';
9
+
10
+ import type {
11
+ PublishingPlan,
12
+ VersionChange,
13
+ DependencyUpdate,
14
+ VerboseData,
15
+ VerboseChangesetDetail,
16
+ VerboseIteration,
17
+ VerbosePropagationChain,
18
+ VerboseGraphSummary,
19
+ } from './publishing_plan.js';
20
+
21
+ export interface LogPlanOptions {
22
+ verbose?: boolean;
23
+ }
24
+
25
+ const SEPARATOR = '────────────────────────────────────────';
26
+
27
+ /**
28
+ * Logs a section header with separators.
29
+ */
30
+ const log_section_header = (title: string, log: Logger): void => {
31
+ log.info(st('dim', SEPARATOR));
32
+ log.info(st('dim', title));
33
+ log.info(st('dim', SEPARATOR));
34
+ };
35
+
36
+ /**
37
+ * Formats a dependency update as diff-style output.
38
+ * Shows - old / + new for the dependency version.
39
+ */
40
+ const format_dep_diff = (dep_name: string, current: string, next: string): Array<string> => {
41
+ return [
42
+ st('red', ` - "${dep_name}": "${current}"`),
43
+ st('green', ` + "${dep_name}": "${next}"`),
44
+ ];
45
+ };
46
+
47
+ /**
48
+ * Gets dependency updates for a specific package, grouped by dependency name.
49
+ */
50
+ const get_updates_for_package = (
51
+ pkg_name: string,
52
+ dependency_updates: Array<DependencyUpdate>,
53
+ ): Map<string, Array<DependencyUpdate>> => {
54
+ const updates: Map<string, Array<DependencyUpdate>> = new Map();
55
+ for (const update of dependency_updates) {
56
+ if (update.dependent_package === pkg_name) {
57
+ const dep_updates = updates.get(update.updated_dependency) || [];
58
+ dep_updates.push(update);
59
+ updates.set(update.updated_dependency, dep_updates);
60
+ }
61
+ }
62
+ return updates;
63
+ };
64
+
65
+ /**
66
+ * Logs a single version change with diff-style dependency updates.
67
+ */
68
+ const log_version_change_with_diffs = (
69
+ change: VersionChange,
70
+ index: number,
71
+ total: number,
72
+ dependency_updates: Array<DependencyUpdate>,
73
+ breaking_cascades: Map<string, Array<string>>,
74
+ log: Logger,
75
+ ): void => {
76
+ const breaking_indicator = change.breaking ? st('red', ' BREAKING') : '';
77
+ const position = st('dim', `[${index + 1}/${total}]`);
78
+
79
+ // Determine scenario label
80
+ let scenario_label = '';
81
+ if (change.needs_bump_escalation) {
82
+ scenario_label = st('yellow', ` [${change.existing_bump} → ${change.required_bump}]`);
83
+ } else if (change.will_generate_changeset) {
84
+ scenario_label = st('cyan', ' [auto-changeset]');
85
+ }
86
+
87
+ // Main version line
88
+ log.info(
89
+ `${position} ${change.package_name}: ${change.from} → ${st('green', change.to)} ` +
90
+ `(${change.bump_type})${scenario_label}${breaking_indicator}`,
91
+ );
92
+
93
+ // Show escalation reason
94
+ if (change.needs_bump_escalation) {
95
+ log.info(
96
+ st(
97
+ 'dim',
98
+ ` changesets specify ${change.existing_bump}, dependencies require ${change.required_bump}`,
99
+ ),
100
+ );
101
+ }
102
+
103
+ // Show trigger reason for auto-changesets
104
+ if (change.will_generate_changeset) {
105
+ // Find what triggered this
106
+ const triggers: Array<string> = [];
107
+ for (const [pkg, affected] of breaking_cascades) {
108
+ if (affected.includes(change.package_name)) {
109
+ triggers.push(`${pkg} (BREAKING)`);
110
+ }
111
+ }
112
+ if (triggers.length > 0) {
113
+ log.info(st('dim', ` triggered by: ${triggers.join(', ')}`));
114
+ }
115
+ }
116
+
117
+ // Show dependency diffs for this package
118
+ const pkg_updates = get_updates_for_package(change.package_name, dependency_updates);
119
+ if (pkg_updates.size > 0) {
120
+ for (const [dep_name, updates] of pkg_updates) {
121
+ if (updates.length === 0) continue;
122
+ const update = updates[0]!;
123
+ for (const line of format_dep_diff(dep_name, update.current_version, update.new_version)) {
124
+ log.info(line);
125
+ }
126
+ }
127
+ }
128
+ };
129
+
130
+ /**
131
+ * Logs a complete publishing plan to the console.
132
+ *
133
+ * Displays errors, publishing order, version changes grouped by scenario,
134
+ * dependency-only updates, warnings, and a summary.
135
+ */
136
+ export const log_publishing_plan = (
137
+ plan: PublishingPlan,
138
+ log: Logger,
139
+ options: LogPlanOptions = {},
140
+ ): void => {
141
+ const {
142
+ publishing_order,
143
+ version_changes,
144
+ dependency_updates,
145
+ breaking_cascades,
146
+ warnings,
147
+ info,
148
+ errors,
149
+ } = plan;
150
+
151
+ // Errors first (blocking issues)
152
+ if (errors.length > 0) {
153
+ log.error(st('red', 'Errors:'));
154
+ for (const error of errors) {
155
+ log.error(` ${error}`);
156
+ }
157
+ log.info('');
158
+ }
159
+
160
+ // Publishing order
161
+ if (publishing_order.length > 0) {
162
+ log.info(st('cyan', 'Publishing Order:'));
163
+ log.info(` ${publishing_order.join(' → ')}`);
164
+ log.info('');
165
+ }
166
+
167
+ // Version changes with diffs
168
+ if (version_changes.length > 0) {
169
+ // Sort by publishing order
170
+ const ordered_changes = [...version_changes].sort((a, b) => {
171
+ const idx_a = publishing_order.indexOf(a.package_name);
172
+ const idx_b = publishing_order.indexOf(b.package_name);
173
+ return idx_a - idx_b;
174
+ });
175
+
176
+ // Separate into groups for headers
177
+ const with_changesets = ordered_changes.filter(
178
+ (vc) => vc.has_changesets && !vc.needs_bump_escalation,
179
+ );
180
+ const with_escalation = ordered_changes.filter((vc) => vc.needs_bump_escalation);
181
+ const with_auto_changesets = ordered_changes.filter((vc) => vc.will_generate_changeset);
182
+
183
+ // Log each group with diff-style output
184
+ if (with_changesets.length > 0) {
185
+ log.info(st('cyan', 'Version Changes (from changesets):'));
186
+ for (const change of with_changesets) {
187
+ const idx = ordered_changes.indexOf(change);
188
+ log_version_change_with_diffs(
189
+ change,
190
+ idx,
191
+ ordered_changes.length,
192
+ dependency_updates,
193
+ breaking_cascades,
194
+ log,
195
+ );
196
+ }
197
+ log.info('');
198
+ }
199
+
200
+ if (with_escalation.length > 0) {
201
+ log.info(st('yellow', 'Version Changes (bump escalation):'));
202
+ for (const change of with_escalation) {
203
+ const idx = ordered_changes.indexOf(change);
204
+ log_version_change_with_diffs(
205
+ change,
206
+ idx,
207
+ ordered_changes.length,
208
+ dependency_updates,
209
+ breaking_cascades,
210
+ log,
211
+ );
212
+ }
213
+ log.info('');
214
+ }
215
+
216
+ if (with_auto_changesets.length > 0) {
217
+ log.info(st('cyan', 'Version Changes (auto-generated):'));
218
+ for (const change of with_auto_changesets) {
219
+ const idx = ordered_changes.indexOf(change);
220
+ log_version_change_with_diffs(
221
+ change,
222
+ idx,
223
+ ordered_changes.length,
224
+ dependency_updates,
225
+ breaking_cascades,
226
+ log,
227
+ );
228
+ }
229
+ log.info('');
230
+ }
231
+ } else {
232
+ log.info(st('dim', 'No packages to publish'));
233
+ log.info('');
234
+ }
235
+
236
+ // Dependency-only updates (no republish) - packages getting dep updates but not publishing
237
+ const dep_only_packages: Set<string> = new Set();
238
+ for (const update of dependency_updates) {
239
+ const has_version_change = version_changes.some(
240
+ (vc) => vc.package_name === update.dependent_package,
241
+ );
242
+ if (!has_version_change) {
243
+ dep_only_packages.add(update.dependent_package);
244
+ }
245
+ }
246
+
247
+ if (dep_only_packages.size > 0) {
248
+ log.info(st('dim', 'Dependency Updates (no republish):'));
249
+ for (const pkg of dep_only_packages) {
250
+ log.info(st('dim', ` ${pkg}:`));
251
+ const pkg_updates = get_updates_for_package(pkg, dependency_updates);
252
+ for (const [dep_name, updates] of pkg_updates) {
253
+ if (updates.length === 0) continue;
254
+ const update = updates[0]!;
255
+ // Use dim styling for non-publishing packages
256
+ log.info(st('dim', ` - "${dep_name}": "${update.current_version}"`));
257
+ log.info(st('dim', ` + "${dep_name}": "${update.new_version}"`));
258
+ }
259
+ }
260
+ log.info('');
261
+ }
262
+
263
+ // Warnings
264
+ if (warnings.length > 0) {
265
+ log.warn(st('yellow', 'Warnings:'));
266
+ for (const warning of warnings) {
267
+ log.warn(` ${warning}`);
268
+ }
269
+ log.info('');
270
+ }
271
+
272
+ // Info (packages with no changes)
273
+ if (info.length > 0) {
274
+ log.info(st('dim', `No changes: ${info.join(', ')}`));
275
+ log.info('');
276
+ }
277
+
278
+ // Summary
279
+ const major_count = version_changes.filter((vc) => vc.breaking).length;
280
+ const auto_count = version_changes.filter((vc) => vc.will_generate_changeset).length;
281
+ log.info(st('cyan', 'Summary:'));
282
+ log.info(` ${version_changes.length} packages to publish`);
283
+ if (auto_count > 0) {
284
+ log.info(` ${auto_count} auto-generated changesets`);
285
+ }
286
+ if (major_count > 0) {
287
+ log.info(st('yellow', ` ${major_count} breaking changes`));
288
+ }
289
+ log.info(` ${dep_only_packages.size} packages with dependency-only updates`);
290
+ if (warnings.length > 0) {
291
+ log.info(st('yellow', ` ${warnings.length} warnings`));
292
+ }
293
+ if (errors.length > 0) {
294
+ log.info(st('red', ` ${errors.length} errors`));
295
+ }
296
+
297
+ // Verbose output
298
+ if (options.verbose && plan.verbose_data) {
299
+ log_verbose(plan.verbose_data, log);
300
+ }
301
+ };
302
+
303
+ // ============================================================================
304
+ // Verbose logging functions
305
+ // ============================================================================
306
+
307
+ /**
308
+ * Logs all verbose sections in order.
309
+ */
310
+ const log_verbose = (data: VerboseData, log: Logger): void => {
311
+ log.info(''); // Blank line before verbose sections
312
+
313
+ log_verbose_changeset_details(data.changeset_details, log);
314
+ log_verbose_iteration_details(data.iterations, data.total_iterations, log);
315
+ log_verbose_propagation_chains(data.propagation_chains, log);
316
+ log_verbose_graph_summary(data.graph_summary, log);
317
+ };
318
+
319
+ /**
320
+ * Logs changeset file details per package.
321
+ */
322
+ const log_verbose_changeset_details = (
323
+ details: Array<VerboseChangesetDetail>,
324
+ log: Logger,
325
+ ): void => {
326
+ if (details.length === 0) return;
327
+
328
+ log_section_header('Changeset Details', log);
329
+
330
+ for (const pkg of details) {
331
+ log.info(st('dim', `${pkg.package_name}:`));
332
+ for (const file of pkg.files) {
333
+ const summary_text = file.summary ? `: "${file.summary}"` : '';
334
+ log.info(st('dim', ` .changeset/${file.filename} (${file.bump_type})${summary_text}`));
335
+ }
336
+ }
337
+ log.info('');
338
+ };
339
+
340
+ /**
341
+ * Logs fixed-point iteration details.
342
+ */
343
+ const log_verbose_iteration_details = (
344
+ iterations: Array<VerboseIteration>,
345
+ total: number,
346
+ log: Logger,
347
+ ): void => {
348
+ if (iterations.length === 0) return;
349
+
350
+ log_section_header('Fixed-Point Iteration', log);
351
+
352
+ for (const iter of iterations) {
353
+ if (iter.new_changes === 0 && iter.packages.length === 0) {
354
+ log.info(st('dim', `Iteration ${iter.iteration}:`));
355
+ log.info(st('dim', ' No new changes'));
356
+ continue;
357
+ }
358
+
359
+ log.info(st('dim', `Iteration ${iter.iteration}:`));
360
+ for (const pkg of iter.packages) {
361
+ const parts: Array<string> = [];
362
+
363
+ // Changeset info
364
+ if (pkg.changeset_count > 0) {
365
+ parts.push(`changesets=${pkg.changeset_count}`);
366
+ if (pkg.bump_from_changesets) {
367
+ parts.push(`bump=${pkg.bump_from_changesets}`);
368
+ }
369
+ } else {
370
+ parts.push('no changesets');
371
+ }
372
+
373
+ // Required bump from deps
374
+ if (pkg.required_bump && pkg.triggering_dep) {
375
+ parts.push(`required=${pkg.required_bump} (from ${pkg.triggering_dep})`);
376
+ }
377
+
378
+ // Action
379
+ if (pkg.action === 'auto_changeset') {
380
+ parts.push('→ auto-generate');
381
+ } else if (pkg.action === 'escalation') {
382
+ parts.push('→ escalation');
383
+ } else if (pkg.action === 'publish') {
384
+ // Regular publish, no special label needed
385
+ }
386
+
387
+ // Version
388
+ if (pkg.version_to) {
389
+ const breaking_label = pkg.is_breaking ? ' (BREAKING)' : '';
390
+ parts.push(`→ ${pkg.version_to}${breaking_label}`);
391
+ }
392
+
393
+ log.info(st('dim', ` ${pkg.name}: ${parts.join(', ')}`));
394
+ }
395
+
396
+ if (iter.new_changes > 0) {
397
+ log.info(st('dim', ` New changes: ${iter.new_changes}`));
398
+ }
399
+ }
400
+
401
+ log.info(st('dim', `Converged after ${total} iteration(s)`));
402
+ log.info('');
403
+ };
404
+
405
+ /**
406
+ * Logs breaking change propagation chains as tree structure.
407
+ */
408
+ const log_verbose_propagation_chains = (
409
+ chains: Array<VerbosePropagationChain>,
410
+ log: Logger,
411
+ ): void => {
412
+ if (chains.length === 0) return;
413
+
414
+ log_section_header('Breaking Change Propagation', log);
415
+
416
+ for (const chain of chains) {
417
+ log.info(st('dim', `${chain.source} (source)`));
418
+
419
+ for (let i = 0; i < chain.chain.length; i++) {
420
+ const item = chain.chain[i]!;
421
+ const indent = ' '.repeat(i);
422
+ const connector = '└─';
423
+ log.info(
424
+ st('dim', `${indent}${connector} ${item.pkg} (${item.dep_type} dep) → ${item.action}`),
425
+ );
426
+ }
427
+ }
428
+ log.info('');
429
+ };
430
+
431
+ /**
432
+ * Logs dependency graph summary.
433
+ */
434
+ const log_verbose_graph_summary = (summary: VerboseGraphSummary, log: Logger): void => {
435
+ log_section_header('Dependency Graph', log);
436
+
437
+ log.info(
438
+ st(
439
+ 'dim',
440
+ `${summary.package_count} packages, ${summary.internal_dep_count} internal dependencies`,
441
+ ),
442
+ );
443
+ log.info('');
444
+
445
+ // Production/Peer edges
446
+ if (summary.prod_peer_edges.length > 0) {
447
+ log.info(st('dim', 'Production/Peer:'));
448
+ for (const edge of summary.prod_peer_edges) {
449
+ log.info(st('dim', ` ${edge.from} → ${edge.to} (${edge.type})`));
450
+ }
451
+ } else {
452
+ log.info(st('dim', 'Production/Peer: (none)'));
453
+ }
454
+
455
+ // Dev edges
456
+ if (summary.dev_edges.length > 0) {
457
+ log.info(st('dim', 'Dev:'));
458
+ for (const edge of summary.dev_edges) {
459
+ log.info(st('dim', ` ${edge.from} → ${edge.to}`));
460
+ }
461
+ } else {
462
+ log.info(st('dim', 'Dev: (none)'));
463
+ }
464
+
465
+ log.info('');
466
+ log.info(
467
+ st('dim', `Cycles: ${summary.prod_cycle_count} production, ${summary.dev_cycle_count} dev`),
468
+ );
469
+ log.info('');
470
+ };
@@ -0,0 +1,95 @@
1
+ import {create_context} from '@fuzdev/fuz_ui/context_helpers.js';
2
+ import type {LibraryJson} from '@fuzdev/fuz_util/library_json.js';
3
+ import type {PackageJson} from '@fuzdev/fuz_util/package_json.js';
4
+ import type {SourceJson} from '@fuzdev/fuz_util/source_json.js';
5
+ import type {Url} from '@fuzdev/fuz_util/url.js';
6
+ import {Library} from '@fuzdev/fuz_ui/library.svelte.js';
7
+ import type {Module} from '@fuzdev/fuz_ui/module.svelte.js';
8
+
9
+ import {GithubCheckRunsItem, type GithubPullRequest} from './github.js';
10
+
11
+ /**
12
+ * Serialized repo data as stored in repos.ts (JSON).
13
+ */
14
+ export interface RepoJson {
15
+ library_json: LibraryJson;
16
+ check_runs: GithubCheckRunsItem | null;
17
+ pull_requests: Array<GithubPullRequest> | null;
18
+ }
19
+
20
+ /**
21
+ * Runtime repo with Library composition for package metadata.
22
+ *
23
+ * Wraps a Library instance and adds GitHub-specific data (CI status, PRs).
24
+ * Convenience getters delegate to `this.library.*` for common properties.
25
+ */
26
+ export class Repo {
27
+ readonly library: Library;
28
+ check_runs: GithubCheckRunsItem | null;
29
+ pull_requests: Array<GithubPullRequest> | null;
30
+
31
+ // Convenience getters delegating to library
32
+ get name(): string {
33
+ return this.library.name;
34
+ }
35
+ get repo_name(): string {
36
+ return this.library.repo_name;
37
+ }
38
+ get repo_url(): Url {
39
+ return this.library.repo_url;
40
+ }
41
+ get owner_name(): string | null {
42
+ return this.library.owner_name;
43
+ }
44
+ get homepage_url(): Url | null {
45
+ return this.library.homepage_url;
46
+ }
47
+ get logo_url(): Url | null {
48
+ return this.library.logo_url;
49
+ }
50
+ get logo_alt(): string {
51
+ return this.library.logo_alt;
52
+ }
53
+ get published(): boolean {
54
+ return this.library.published;
55
+ }
56
+ get npm_url(): Url | null {
57
+ return this.library.npm_url;
58
+ }
59
+ get changelog_url(): Url | null {
60
+ return this.library.changelog_url;
61
+ }
62
+ get package_json(): PackageJson {
63
+ return this.library.package_json;
64
+ }
65
+ get source_json(): SourceJson {
66
+ return this.library.source_json;
67
+ }
68
+ get modules(): Array<Module> {
69
+ return this.library.modules;
70
+ }
71
+ get org_url(): string | null {
72
+ return this.library.org_url;
73
+ }
74
+
75
+ constructor(repo_json: RepoJson) {
76
+ this.library = new Library(repo_json.library_json);
77
+ this.check_runs = repo_json.check_runs;
78
+ this.pull_requests = repo_json.pull_requests;
79
+ }
80
+ }
81
+
82
+ export interface Repos {
83
+ repo: Repo;
84
+ repos: Array<Repo>;
85
+ }
86
+
87
+ export const repos_context = create_context<Repos>();
88
+
89
+ export const repos_parse = (repos: Array<Repo>, homepage_url: string): Repos => {
90
+ // We expect to find this because it's sourced from the local package.json
91
+ const repo = repos.find((d) => d.homepage_url === homepage_url);
92
+ if (!repo) throw Error(`Cannot find repo with homepage_url: ${homepage_url}`);
93
+
94
+ return {repo, repos};
95
+ };