@fuzdev/fuz_gitops 0.72.0 → 0.74.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 (122) hide show
  1. package/README.md +15 -8
  2. package/dist/PageHeader.svelte +3 -3
  3. package/dist/PageHeader.svelte.d.ts +3 -3
  4. package/dist/PageHeader.svelte.d.ts.map +1 -1
  5. package/dist/changeset_generator.d.ts.map +1 -1
  6. package/dist/changeset_generator.js +2 -16
  7. package/dist/changeset_reader.d.ts +1 -1
  8. package/dist/changeset_reader.d.ts.map +1 -1
  9. package/dist/changeset_reader.js +1 -1
  10. package/dist/dependency_graph.d.ts.map +1 -1
  11. package/dist/dependency_graph.js +6 -6
  12. package/dist/fetch_repo_data.d.ts.map +1 -1
  13. package/dist/fetch_repo_data.js +3 -2
  14. package/dist/git_operations.d.ts +5 -0
  15. package/dist/git_operations.d.ts.map +1 -1
  16. package/dist/git_operations.js +5 -0
  17. package/dist/gitops_analyze.task.d.ts +1 -0
  18. package/dist/gitops_analyze.task.d.ts.map +1 -1
  19. package/dist/gitops_analyze.task.js +12 -17
  20. package/dist/gitops_plan.task.d.ts +1 -0
  21. package/dist/gitops_plan.task.d.ts.map +1 -1
  22. package/dist/gitops_plan.task.js +9 -2
  23. package/dist/gitops_publish.task.d.ts +2 -2
  24. package/dist/gitops_publish.task.d.ts.map +1 -1
  25. package/dist/gitops_publish.task.js +83 -28
  26. package/dist/gitops_run.task.d.ts +2 -1
  27. package/dist/gitops_run.task.d.ts.map +1 -1
  28. package/dist/gitops_run.task.js +40 -7
  29. package/dist/gitops_sync.task.d.ts +1 -0
  30. package/dist/gitops_sync.task.d.ts.map +1 -1
  31. package/dist/gitops_sync.task.js +16 -2
  32. package/dist/gitops_task_helpers.d.ts +16 -2
  33. package/dist/gitops_task_helpers.d.ts.map +1 -1
  34. package/dist/gitops_task_helpers.js +11 -3
  35. package/dist/gitops_validate.task.d.ts +1 -0
  36. package/dist/gitops_validate.task.d.ts.map +1 -1
  37. package/dist/gitops_validate.task.js +20 -20
  38. package/dist/graph_validation.d.ts +16 -1
  39. package/dist/graph_validation.d.ts.map +1 -1
  40. package/dist/graph_validation.js +15 -0
  41. package/dist/local_repo.d.ts +25 -12
  42. package/dist/local_repo.d.ts.map +1 -1
  43. package/dist/local_repo.js +99 -79
  44. package/dist/log_helpers.d.ts +0 -5
  45. package/dist/log_helpers.d.ts.map +1 -1
  46. package/dist/log_helpers.js +8 -19
  47. package/dist/multi_repo_publisher.d.ts +21 -2
  48. package/dist/multi_repo_publisher.d.ts.map +1 -1
  49. package/dist/multi_repo_publisher.js +279 -287
  50. package/dist/operations.d.ts +6 -26
  51. package/dist/operations.d.ts.map +1 -1
  52. package/dist/operations_defaults.d.ts.map +1 -1
  53. package/dist/operations_defaults.js +4 -20
  54. package/dist/output_helpers.d.ts.map +1 -1
  55. package/dist/output_helpers.js +7 -6
  56. package/dist/paths.d.ts +0 -4
  57. package/dist/paths.d.ts.map +1 -1
  58. package/dist/paths.js +0 -4
  59. package/dist/publish_gate.d.ts +44 -0
  60. package/dist/publish_gate.d.ts.map +1 -0
  61. package/dist/publish_gate.js +32 -0
  62. package/dist/publish_steps.d.ts +60 -0
  63. package/dist/publish_steps.d.ts.map +1 -0
  64. package/dist/publish_steps.js +113 -0
  65. package/dist/publishing_event.d.ts +12 -19
  66. package/dist/publishing_event.d.ts.map +1 -1
  67. package/dist/publishing_event.js +16 -25
  68. package/dist/publishing_plan.d.ts +1 -2
  69. package/dist/publishing_plan.d.ts.map +1 -1
  70. package/dist/publishing_plan.js +21 -4
  71. package/dist/publishing_plan_helpers.d.ts +1 -1
  72. package/dist/publishing_plan_helpers.d.ts.map +1 -1
  73. package/dist/publishing_plan_helpers.js +2 -15
  74. package/dist/publishing_plan_logging.d.ts.map +1 -1
  75. package/dist/publishing_plan_logging.js +15 -23
  76. package/dist/repo.svelte.d.ts +10 -6
  77. package/dist/repo.svelte.d.ts.map +1 -1
  78. package/dist/repo.svelte.js +6 -14
  79. package/dist/version_utils.d.ts +17 -3
  80. package/dist/version_utils.d.ts.map +1 -1
  81. package/dist/version_utils.js +22 -0
  82. package/package.json +14 -15
  83. package/src/lib/changeset_generator.ts +10 -19
  84. package/src/lib/changeset_reader.ts +2 -3
  85. package/src/lib/dependency_graph.ts +6 -8
  86. package/src/lib/fetch_repo_data.ts +3 -2
  87. package/src/lib/git_operations.ts +5 -0
  88. package/src/lib/gitops_analyze.task.ts +18 -23
  89. package/src/lib/gitops_plan.task.ts +10 -2
  90. package/src/lib/gitops_publish.task.ts +92 -27
  91. package/src/lib/gitops_run.task.ts +39 -7
  92. package/src/lib/gitops_sync.task.ts +18 -3
  93. package/src/lib/gitops_task_helpers.ts +20 -3
  94. package/src/lib/gitops_validate.task.ts +30 -22
  95. package/src/lib/graph_validation.ts +26 -0
  96. package/src/lib/local_repo.ts +124 -88
  97. package/src/lib/log_helpers.ts +8 -26
  98. package/src/lib/multi_repo_publisher.ts +336 -353
  99. package/src/lib/operations.ts +6 -22
  100. package/src/lib/operations_defaults.ts +4 -20
  101. package/src/lib/output_helpers.ts +7 -6
  102. package/src/lib/paths.ts +0 -5
  103. package/src/lib/publish_gate.ts +53 -0
  104. package/src/lib/publish_steps.ts +149 -0
  105. package/src/lib/publishing_event.ts +16 -25
  106. package/src/lib/publishing_plan.ts +26 -6
  107. package/src/lib/publishing_plan_helpers.ts +5 -18
  108. package/src/lib/publishing_plan_logging.ts +19 -39
  109. package/src/lib/repo.svelte.ts +12 -15
  110. package/src/lib/version_utils.ts +30 -9
  111. package/dist/npm_install_helpers.d.ts +0 -23
  112. package/dist/npm_install_helpers.d.ts.map +0 -1
  113. package/dist/npm_install_helpers.js +0 -60
  114. package/dist/semver.d.ts +0 -26
  115. package/dist/semver.d.ts.map +0 -1
  116. package/dist/semver.js +0 -137
  117. package/dist/serialization_types.d.ts +0 -59
  118. package/dist/serialization_types.d.ts.map +0 -1
  119. package/dist/serialization_types.js +0 -42
  120. package/src/lib/npm_install_helpers.ts +0 -85
  121. package/src/lib/semver.ts +0 -170
  122. package/src/lib/serialization_types.ts +0 -92
@@ -9,98 +9,119 @@ import { map_concurrent_settled } from '@fuzdev/fuz_util/async.js';
9
9
  import { default_git_operations, default_npm_operations } from './operations_defaults.js';
10
10
  import { GITOPS_CONCURRENCY_DEFAULT } from './gitops_constants.js';
11
11
  /**
12
- * Loads repo data with automatic syncing and dependency management.
12
+ * Loads repo data, optionally syncing the working tree first.
13
13
  *
14
- * Workflow:
14
+ * When `sync` is `false` (the default for read-only diagnostics like
15
+ * `gitops_analyze`/`gitops_plan`), the repo is loaded exactly as it sits on
16
+ * disk — no branch switch, pull, install, or clean-workspace check. This makes
17
+ * those commands safe to run on an active workspace with uncommitted changes or
18
+ * feature branches checked out.
19
+ *
20
+ * When `sync` is `true` (used by `gitops_sync`), the working tree is brought in
21
+ * line with the configured branch first:
15
22
  * 1. Records current commit hash (for detecting changes)
16
- * 2. Switches to target branch if needed (requires clean workspace)
23
+ * 2. Switches to target branch if needed (requires clean workspace unless `allow_dirty`)
17
24
  * 3. Pulls latest changes from remote (skipped for local-only repos)
18
- * 4. Validates workspace is clean after pull
25
+ * 4. Validates workspace is clean after pull (skipped if `allow_dirty`)
19
26
  * 5. Auto-installs dependencies if `package.json` changed
27
+ *
28
+ * Either way it then:
20
29
  * 6. Loads `library_json` via `library_load_from_repo` (svelte-docinfo analysis)
21
30
  * 7. Creates `Library` and extracts dependency maps
22
31
  *
23
- * This ensures repos are always in sync with their configured branch
24
- * before being used by gitops commands.
25
- *
26
- * @throws {TaskError} if workspace dirty, branch switch fails, install fails, or analysis fails
32
+ * @param sync - sync the working tree to the configured branch before loading (default `true`)
33
+ * @param allow_dirty - when syncing, tolerate uncommitted changes instead of throwing (default `false`)
34
+ * @throws {TaskError} if syncing fails (dirty workspace, branch switch, install) or analysis fails
27
35
  */
28
- export const local_repo_load = async ({ local_repo_path, log: _log, git_ops = default_git_operations, npm_ops = default_npm_operations, }) => {
36
+ export const local_repo_load = async ({ local_repo_path, log: _log, git_ops = default_git_operations, npm_ops = default_npm_operations, sync = true, allow_dirty = false, }) => {
29
37
  const { repo_config, repo_dir, repo_name, repo_git_ssh_url } = local_repo_path;
30
- // Record commit hash before any changes
31
- const commit_before_result = await git_ops.current_commit_hash({ cwd: repo_dir });
32
- if (!commit_before_result.ok) {
33
- throw new TaskError(`Failed to get commit hash in ${repo_dir}: ${commit_before_result.message}`);
34
- }
35
- const commit_before = commit_before_result.value;
36
- // Switch to target branch if needed
37
- const branch_result = await git_ops.current_branch_name({ cwd: repo_dir });
38
- if (!branch_result.ok) {
39
- throw new TaskError(`Failed to get current branch in ${repo_dir}: ${branch_result.message}`);
40
- }
41
- const switched_branches = branch_result.value !== repo_config.branch;
42
- if (switched_branches) {
43
- const clean_result = await git_ops.check_clean_workspace({ cwd: repo_dir });
44
- if (!clean_result.ok) {
45
- throw new TaskError(`Failed to check workspace in ${repo_dir}: ${clean_result.message}`);
38
+ if (sync) {
39
+ // Record commit hash before any changes
40
+ const commit_before_result = await git_ops.current_commit_hash({ cwd: repo_dir });
41
+ if (!commit_before_result.ok) {
42
+ throw new TaskError(`Failed to get commit hash in ${repo_dir}: ${commit_before_result.message}`);
46
43
  }
47
- if (!clean_result.value) {
48
- throw new TaskError(`Repo ${repo_dir} is not on branch "${repo_config.branch}" and the workspace is unclean, blocking switch`);
44
+ const commit_before = commit_before_result.value;
45
+ // Switch to target branch if needed
46
+ const branch_result = await git_ops.current_branch_name({ cwd: repo_dir });
47
+ if (!branch_result.ok) {
48
+ throw new TaskError(`Failed to get current branch in ${repo_dir}: ${branch_result.message}`);
49
49
  }
50
- const checkout_result = await git_ops.checkout({ branch: repo_config.branch, cwd: repo_dir });
51
- if (!checkout_result.ok) {
52
- throw new TaskError(`Failed to checkout branch "${repo_config.branch}" in ${repo_dir}: ${checkout_result.message}`);
50
+ const switched_branches = branch_result.value !== repo_config.branch;
51
+ if (switched_branches) {
52
+ // Guard the switch on a clean workspace unless the caller opts into `allow_dirty`,
53
+ // in which case we let `git checkout` itself fail loudly if it can't proceed.
54
+ if (!allow_dirty) {
55
+ const clean_result = await git_ops.check_clean_workspace({ cwd: repo_dir });
56
+ if (!clean_result.ok) {
57
+ throw new TaskError(`Failed to check workspace in ${repo_dir}: ${clean_result.message}`);
58
+ }
59
+ if (!clean_result.value) {
60
+ throw new TaskError(`Repo ${repo_dir} is not on branch "${repo_config.branch}" and the workspace is unclean, blocking switch`);
61
+ }
62
+ }
63
+ const checkout_result = await git_ops.checkout({ branch: repo_config.branch, cwd: repo_dir });
64
+ if (!checkout_result.ok) {
65
+ throw new TaskError(`Failed to checkout branch "${repo_config.branch}" in ${repo_dir}: ${checkout_result.message}`);
66
+ }
53
67
  }
54
- }
55
- // Only pull if remote exists (skip for local-only repos, test fixtures)
56
- const origin_result = await git_ops.has_remote({ remote: 'origin', cwd: repo_dir });
57
- if (!origin_result.ok) {
58
- throw new TaskError(`Failed to check for remote in ${repo_dir}: ${origin_result.message}`);
59
- }
60
- if (origin_result.value) {
61
- const pull_result = await git_ops.pull({ cwd: repo_dir });
62
- if (!pull_result.ok) {
63
- throw new TaskError(`Failed to pull in ${repo_dir}: ${pull_result.message}`);
68
+ // Only pull if remote exists (skip for local-only repos, test fixtures)
69
+ const origin_result = await git_ops.has_remote({ remote: 'origin', cwd: repo_dir });
70
+ if (!origin_result.ok) {
71
+ throw new TaskError(`Failed to check for remote in ${repo_dir}: ${origin_result.message}`);
64
72
  }
65
- }
66
- // Check clean workspace after pull to ensure we're in a good state
67
- const clean_after_result = await git_ops.check_clean_workspace({ cwd: repo_dir });
68
- if (!clean_after_result.ok) {
69
- throw new TaskError(`Failed to check workspace in ${repo_dir}: ${clean_after_result.message}`);
70
- }
71
- if (!clean_after_result.value) {
72
- throw new TaskError(`Workspace ${repo_dir} is unclean after pulling branch "${repo_config.branch}"`);
73
- }
74
- // Record commit hash after pull
75
- const commit_after_result = await git_ops.current_commit_hash({ cwd: repo_dir });
76
- if (!commit_after_result.ok) {
77
- throw new TaskError(`Failed to get commit hash in ${repo_dir}: ${commit_after_result.message}`);
78
- }
79
- const commit_after = commit_after_result.value;
80
- // Track if we got new commits
81
- const got_new_commits = commit_before !== commit_after;
82
- // Only install if package.json changed
83
- if (got_new_commits) {
84
- const changed_result = await git_ops.has_file_changed({
85
- from_commit: commit_before,
86
- to_commit: commit_after,
87
- file_path: 'package.json',
88
- cwd: repo_dir,
89
- });
90
- if (!changed_result.ok) {
91
- throw new TaskError(`Failed to check if package.json changed in ${repo_dir}: ${changed_result.message}`);
92
- }
93
- if (changed_result.value) {
94
- const install_result = await npm_ops.install({ cwd: repo_dir });
95
- if (!install_result.ok) {
96
- throw new TaskError(`Failed to install dependencies in ${repo_dir}: ${install_result.message}${install_result.stderr ? `\n${install_result.stderr}` : ''}`);
73
+ if (origin_result.value) {
74
+ // Pull the configured branch explicitly. Without a branch, `git pull origin`
75
+ // targets the remote's default branch (origin/HEAD), which for a repo checked
76
+ // out on a non-default branch rebases the wrong branch onto it.
77
+ const pull_result = await git_ops.pull({ branch: repo_config.branch, cwd: repo_dir });
78
+ if (!pull_result.ok) {
79
+ throw new TaskError(`Failed to pull in ${repo_dir}: ${pull_result.message}`);
80
+ }
81
+ }
82
+ // Check clean workspace after pull to ensure we're in a good state
83
+ // (skipped when `allow_dirty`, since uncommitted changes are expected then)
84
+ if (!allow_dirty) {
85
+ const clean_after_result = await git_ops.check_clean_workspace({ cwd: repo_dir });
86
+ if (!clean_after_result.ok) {
87
+ throw new TaskError(`Failed to check workspace in ${repo_dir}: ${clean_after_result.message}`);
88
+ }
89
+ if (!clean_after_result.value) {
90
+ throw new TaskError(`Workspace ${repo_dir} is unclean after pulling branch "${repo_config.branch}"`);
91
+ }
92
+ }
93
+ // Record commit hash after pull
94
+ const commit_after_result = await git_ops.current_commit_hash({ cwd: repo_dir });
95
+ if (!commit_after_result.ok) {
96
+ throw new TaskError(`Failed to get commit hash in ${repo_dir}: ${commit_after_result.message}`);
97
+ }
98
+ const commit_after = commit_after_result.value;
99
+ // Track if we got new commits
100
+ const got_new_commits = commit_before !== commit_after;
101
+ // Only install if package.json changed
102
+ if (got_new_commits) {
103
+ const changed_result = await git_ops.has_file_changed({
104
+ from_commit: commit_before,
105
+ to_commit: commit_after,
106
+ file_path: 'package.json',
107
+ cwd: repo_dir,
108
+ });
109
+ if (!changed_result.ok) {
110
+ throw new TaskError(`Failed to check if package.json changed in ${repo_dir}: ${changed_result.message}`);
111
+ }
112
+ if (changed_result.value) {
113
+ const install_result = await npm_ops.install({ cwd: repo_dir });
114
+ if (!install_result.ok) {
115
+ throw new TaskError(`Failed to install dependencies in ${repo_dir}: ${install_result.message}${install_result.stderr ? `\n${install_result.stderr}` : ''}`);
116
+ }
97
117
  }
98
118
  }
99
119
  }
100
120
  // Load library metadata via svelte-docinfo analysis (cached under `.gro/library.json`).
101
121
  let library_json;
122
+ let package_json;
102
123
  try {
103
- library_json = await library_load_from_repo(repo_dir, { log: _log });
124
+ ({ library_json, package_json } = await library_load_from_repo(repo_dir, { log: _log }));
104
125
  }
105
126
  catch (err) {
106
127
  const message = err instanceof Error ? err.message : String(err);
@@ -110,13 +131,12 @@ export const local_repo_load = async ({ local_repo_path, log: _log, git_ops = de
110
131
  const library = new Library(library_json);
111
132
  const local_repo = {
112
133
  library,
113
- library_json,
134
+ package_json,
114
135
  repo_dir,
115
136
  repo_git_ssh_url,
116
137
  repo_config,
117
138
  };
118
- // Extract dependencies from package_json
119
- const { package_json } = library;
139
+ // Extract dependencies from the full package_json
120
140
  if (package_json.dependencies) {
121
141
  local_repo.dependencies = new Map(Object.entries(package_json.dependencies));
122
142
  }
@@ -157,18 +177,18 @@ export const local_repos_ensure = async ({ resolved_config, repos_dir, gitops_co
157
177
  }
158
178
  return local_repo_paths;
159
179
  };
160
- export const local_repos_load = async ({ local_repo_paths, log, git_ops = default_git_operations, npm_ops = default_npm_operations, parallel = true, concurrency = GITOPS_CONCURRENCY_DEFAULT, }) => {
180
+ export const local_repos_load = async ({ local_repo_paths, log, git_ops = default_git_operations, npm_ops = default_npm_operations, parallel = true, concurrency = GITOPS_CONCURRENCY_DEFAULT, sync = true, allow_dirty = false, }) => {
161
181
  if (!parallel) {
162
182
  // Sequential loading (original behavior)
163
183
  const loaded = [];
164
184
  for (const local_repo_path of local_repo_paths) {
165
- loaded.push(await local_repo_load({ local_repo_path, log, git_ops, npm_ops }));
185
+ loaded.push(await local_repo_load({ local_repo_path, log, git_ops, npm_ops, sync, allow_dirty }));
166
186
  }
167
187
  return loaded;
168
188
  }
169
189
  // Parallel loading with concurrency limit
170
190
  const results = await map_concurrent_settled(local_repo_paths, concurrency, async (local_repo_path) => {
171
- return local_repo_load({ local_repo_path, log, git_ops, npm_ops });
191
+ return local_repo_load({ local_repo_path, log, git_ops, npm_ops, sync, allow_dirty });
172
192
  });
173
193
  // Check for failures and collect successes
174
194
  const loaded = [];
@@ -35,9 +35,4 @@ export declare const log_production_cycles: (analysis: ReturnType<DependencyGrap
35
35
  * Convenience function that calls all three logging functions in order.
36
36
  */
37
37
  export declare const log_dependency_analysis: (analysis: ReturnType<DependencyGraphBuilder["analyze"]>, log: Logger, indent?: string) => void;
38
- /**
39
- * Logs a simple bulleted list with a header.
40
- * Common pattern for warnings, info messages, and other lists.
41
- */
42
- export declare const log_list: (items: Array<string>, header: string, color: "cyan" | "yellow" | "red" | "dim", log: Logger, log_method?: "info" | "warn" | "error") => void;
43
38
  //# sourceMappingURL=log_helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"log_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/log_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAGpD,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,uBAAuB,CAAC;AAElE;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,KACrD,KAAK,CAAC,MAAM,CASd,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC7B,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,KACrD,KAAK,CAAC,MAAM,CAcd,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GACpC,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,KACrD,KAAK,CAAC,MAAM,CAcd,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,GACrC,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,EACvD,KAAK,MAAM,EACX,eAAW,KACT,IAKF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,GAC1B,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,EACvD,KAAK,MAAM,EACX,eAAW,KACT,IAKF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GACjC,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,EACvD,KAAK,MAAM,EACX,eAAW,KACT,IAKF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GACnC,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,EACvD,KAAK,MAAM,EACX,eAAW,KACT,IAIF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,GACpB,OAAO,KAAK,CAAC,MAAM,CAAC,EACpB,QAAQ,MAAM,EACd,OAAO,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,KAAK,EACxC,KAAK,MAAM,EACX,aAAY,MAAM,GAAG,MAAM,GAAG,OAAgB,KAC5C,IAWF,CAAC"}
1
+ {"version":3,"file":"log_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/log_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAGpD,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,uBAAuB,CAAC;AAElE;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,KACrD,KAAK,CAAC,MAAM,CAYd,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC7B,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,KACrD,KAAK,CAAC,MAAM,CAed,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GACpC,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,KACrD,KAAK,CAAC,MAAM,CAed,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,GACrC,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,EACvD,KAAK,MAAM,EACX,eAAW,KACT,IAKF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,GAC1B,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,EACvD,KAAK,MAAM,EACX,eAAW,KACT,IAKF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GACjC,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,EACvD,KAAK,MAAM,EACX,eAAW,KACT,IAKF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GACnC,UAAU,UAAU,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,EACvD,KAAK,MAAM,EACX,eAAW,KACT,IAIF,CAAC"}
@@ -7,7 +7,10 @@ export const format_wildcard_dependencies = (analysis) => {
7
7
  if (analysis.wildcard_deps.length === 0)
8
8
  return [];
9
9
  const lines = [];
10
- lines.push(st('yellow', `\n⚠️ Found ${analysis.wildcard_deps.length} wildcard dependencies:`));
10
+ // Blank separator as its own line so the logger prefixes it correctly; embedding
11
+ // a leading `\n` in the header would leave the header line unprefixed.
12
+ lines.push('');
13
+ lines.push(st('yellow', `⚠️ Found ${analysis.wildcard_deps.length} wildcard dependencies:`));
11
14
  for (const { pkg, dep, version } of analysis.wildcard_deps) {
12
15
  lines.push(` ${pkg} → ${dep} ${st('red', version)}`);
13
16
  }
@@ -21,7 +24,8 @@ export const format_dev_cycles = (analysis) => {
21
24
  if (analysis.dev_cycles.length === 0)
22
25
  return [];
23
26
  const lines = [];
24
- lines.push(st('dim', `\nℹ️ Found ${analysis.dev_cycles.length} dev circular dependencies (normal, non-blocking):`));
27
+ lines.push('');
28
+ lines.push(st('dim', `ℹ️ Found ${analysis.dev_cycles.length} dev circular dependencies (normal, non-blocking):`));
25
29
  for (const cycle of analysis.dev_cycles) {
26
30
  lines.push(st('dim', ` ${cycle.join(' → ')}`));
27
31
  }
@@ -35,7 +39,8 @@ export const format_production_cycles = (analysis) => {
35
39
  if (analysis.production_cycles.length === 0)
36
40
  return [];
37
41
  const lines = [];
38
- lines.push(st('red', `\n❌ Found ${analysis.production_cycles.length} production/peer circular dependencies (blocks publishing):`));
42
+ lines.push('');
43
+ lines.push(st('red', `❌ Found ${analysis.production_cycles.length} production/peer circular dependencies (blocks publishing):`));
39
44
  for (const cycle of analysis.production_cycles) {
40
45
  lines.push(` ${st('red', cycle.join(' → '))}`);
41
46
  }
@@ -80,19 +85,3 @@ export const log_dependency_analysis = (analysis, log, indent = '') => {
80
85
  log_production_cycles(analysis, log, indent);
81
86
  log_dev_cycles(analysis, log, indent);
82
87
  };
83
- /**
84
- * Logs a simple bulleted list with a header.
85
- * Common pattern for warnings, info messages, and other lists.
86
- */
87
- export const log_list = (items, header, color, log, log_method = 'info') => {
88
- if (items.length === 0)
89
- return;
90
- // Only add newline prefix if header is non-empty
91
- const formatted_header = header ? `\n${header}` : '';
92
- if (formatted_header) {
93
- log[log_method](st(color, formatted_header));
94
- }
95
- for (const item of items) {
96
- log[log_method](st(color, ` • ${item}`));
97
- }
98
- };
@@ -1,16 +1,15 @@
1
1
  import type { Logger } from '@fuzdev/fuz_util/log.js';
2
2
  import type { LocalRepo } from './local_repo.js';
3
3
  import { type VersionStrategy } from './dependency_updater.js';
4
+ import { type DependencyUpdate, type PublishingPlan } from './publishing_plan.js';
4
5
  import type { GitopsOperations } from './operations.js';
5
6
  import { type PublishingEvent, type PublishingRunSummary } from './publishing_event.js';
6
7
  import { type PublishingEventHandler } from './publishing_event_handler.js';
7
8
  export interface PublishingOptions {
8
9
  wetrun: boolean;
9
- update_deps: boolean;
10
10
  version_strategy?: VersionStrategy;
11
11
  deploy?: boolean;
12
12
  max_wait?: number;
13
- skip_install?: boolean;
14
13
  log?: Logger;
15
14
  ops?: GitopsOperations;
16
15
  /** Structured event sink; defaults to capture-only (events surface on the result). */
@@ -37,6 +36,26 @@ export interface PublishingResult {
37
36
  events: Array<PublishingEvent>;
38
37
  /** Tallied outcome, derived from `events`. */
39
38
  summary: PublishingRunSummary;
39
+ /** Plan errors that blocked (wetrun) or would block (dry run) publishing; empty when clean. */
40
+ plan_errors: Array<string>;
41
+ /** Non-blocking plan warnings (e.g. the fixed-point iteration limit). */
42
+ plan_warnings: Array<string>;
40
43
  }
41
44
  export declare const publish_repos: (repos: Array<LocalRepo>, options: PublishingOptions) => Promise<PublishingResult>;
45
+ /**
46
+ * Executes a frozen publishing plan in a single linear pass — the "dumb executor" half of
47
+ * the zap model. The plan is the single source of truth; this re-derives nothing.
48
+ *
49
+ * Fails loud when the plan couldn't be fully computed (`plan.errors`): a wetrun aborts
50
+ * before any side effect, a dry run still reports the partial cascade but returns
51
+ * `ok: false`. This is the single error gate — `--no-plan` can't bypass it.
52
+ */
53
+ export declare const execute_publishing_plan: (repos: Array<LocalRepo>, plan: PublishingPlan, options: PublishingOptions) => Promise<PublishingResult>;
54
+ /**
55
+ * Groups dependency updates by dependent package — `dependent → (dependency → new
56
+ * version)`, the shape `update_package_json` consumes. Restricted by `predicate` (e.g.
57
+ * prod/peer for a given package, or all dev deps) and to dependencies that actually
58
+ * published this run, so a failed/aborted publish never propagates to its dependents.
59
+ */
60
+ export declare const group_dependency_updates: (updates: Array<DependencyUpdate>, published: Map<string, PublishedVersion>, predicate: (update: DependencyUpdate) => boolean) => Map<string, Map<string, string>>;
42
61
  //# sourceMappingURL=multi_repo_publisher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"multi_repo_publisher.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/multi_repo_publisher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAKpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAsB,KAAK,eAAe,EAAC,MAAM,yBAAyB,CAAC;AAIlF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,iBAAiB,CAAC;AAOtD,OAAO,EACN,KAAK,eAAe,EACpB,KAAK,oBAAoB,EAEzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,KAAK,sBAAsB,EAG3B,MAAM,+BAA+B,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,CAAC,EAAE,eAAe,CAAC;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,gBAAgB,CAAC;IACvB,sFAAsF;IACtF,MAAM,CAAC,EAAE,sBAAsB,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACnC,MAAM,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAC,CAAC,CAAC;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IAC/B,8CAA8C;IAC9C,OAAO,EAAE,oBAAoB,CAAC;CAC9B;AAED,eAAO,MAAM,aAAa,GACzB,OAAO,KAAK,CAAC,SAAS,CAAC,EACvB,SAAS,iBAAiB,KACxB,OAAO,CAAC,gBAAgB,CA2Z1B,CAAC"}
1
+ {"version":3,"file":"multi_repo_publisher.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/multi_repo_publisher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAKpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAsB,KAAK,eAAe,EAAC,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAGN,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,iBAAiB,CAAC;AAGtD,OAAO,EACN,KAAK,eAAe,EACpB,KAAK,oBAAoB,EAEzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,KAAK,sBAAsB,EAG3B,MAAM,+BAA+B,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,eAAe,CAAC;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,gBAAgB,CAAC;IACvB,sFAAsF;IACtF,MAAM,CAAC,EAAE,sBAAsB,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACnC,MAAM,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAC,CAAC,CAAC;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IAC/B,8CAA8C;IAC9C,OAAO,EAAE,oBAAoB,CAAC;IAC9B,+FAA+F;IAC/F,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3B,yEAAyE;IACzE,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED,eAAO,MAAM,aAAa,GACzB,OAAO,KAAK,CAAC,SAAS,CAAC,EACvB,SAAS,iBAAiB,KACxB,OAAO,CAAC,gBAAgB,CAO1B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GACnC,OAAO,KAAK,CAAC,SAAS,CAAC,EACvB,MAAM,cAAc,EACpB,SAAS,iBAAiB,KACxB,OAAO,CAAC,gBAAgB,CAwX1B,CAAC;AA+EF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GACpC,SAAS,KAAK,CAAC,gBAAgB,CAAC,EAChC,WAAW,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACxC,WAAW,CAAC,MAAM,EAAE,gBAAgB,KAAK,OAAO,KAC9C,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAcjC,CAAC"}