@hominis/fireforge 0.52.0 → 0.54.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 (248) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/build-info.json +4 -4
  3. package/dist/src/commands/bootstrap.js +2 -3
  4. package/dist/src/commands/build-prelock.d.ts +3 -3
  5. package/dist/src/commands/build-prelock.js +3 -3
  6. package/dist/src/commands/build.js +4 -0
  7. package/dist/src/commands/declarations-report.d.ts +9 -3
  8. package/dist/src/commands/declarations-report.js +18 -11
  9. package/dist/src/commands/discard.js +3 -3
  10. package/dist/src/commands/doctor-furnace-config-sync.js +4 -4
  11. package/dist/src/commands/doctor-furnace.js +5 -5
  12. package/dist/src/commands/export-adopt-check.d.ts +5 -6
  13. package/dist/src/commands/export-adopt-check.js +5 -6
  14. package/dist/src/commands/export-flow.js +6 -9
  15. package/dist/src/commands/export-staged-registration.d.ts +4 -4
  16. package/dist/src/commands/export-staged-registration.js +4 -4
  17. package/dist/src/commands/forward-import-remedy.d.ts +2 -3
  18. package/dist/src/commands/forward-import-remedy.js +2 -3
  19. package/dist/src/commands/furnace/create-dry-run.d.ts +2 -2
  20. package/dist/src/commands/furnace/deploy-fragment-includers.d.ts +2 -2
  21. package/dist/src/commands/furnace/deploy-fragment-includers.js +2 -2
  22. package/dist/src/commands/furnace/index.js +2 -2
  23. package/dist/src/commands/lint.js +4 -4
  24. package/dist/src/commands/patch/compact.js +1 -1
  25. package/dist/src/commands/patch/lint-ignore-from.d.ts +3 -4
  26. package/dist/src/commands/patch/lint-ignore-from.js +3 -4
  27. package/dist/src/commands/patch/move-files-adopt.d.ts +33 -0
  28. package/dist/src/commands/patch/move-files-adopt.js +65 -0
  29. package/dist/src/commands/patch/move-files-create.js +41 -20
  30. package/dist/src/commands/patch/move-files-into.d.ts +18 -0
  31. package/dist/src/commands/patch/move-files-into.js +125 -60
  32. package/dist/src/commands/patch/move-files-retire.d.ts +39 -0
  33. package/dist/src/commands/patch/move-files-retire.js +91 -0
  34. package/dist/src/commands/patch/move-files.d.ts +3 -0
  35. package/dist/src/commands/patch/move-files.js +42 -12
  36. package/dist/src/commands/patch/move-size-tier.d.ts +27 -0
  37. package/dist/src/commands/patch/move-size-tier.js +66 -0
  38. package/dist/src/commands/patch/rename.js +1 -1
  39. package/dist/src/commands/patch/reorder-projection.d.ts +17 -0
  40. package/dist/src/commands/patch/reorder-projection.js +49 -0
  41. package/dist/src/commands/patch/reorder-shift.d.ts +30 -0
  42. package/dist/src/commands/patch/reorder-shift.js +307 -0
  43. package/dist/src/commands/patch/reorder.d.ts +9 -15
  44. package/dist/src/commands/patch/reorder.js +52 -50
  45. package/dist/src/commands/patch/split-plan.d.ts +31 -3
  46. package/dist/src/commands/patch/split-plan.js +36 -12
  47. package/dist/src/commands/patch/split-staged.d.ts +2 -2
  48. package/dist/src/commands/patch/split-staged.js +2 -2
  49. package/dist/src/commands/patch/split.js +18 -5
  50. package/dist/src/commands/patch/staged-dependency-entries.d.ts +9 -1
  51. package/dist/src/commands/patch/staged-dependency-entries.js +32 -4
  52. package/dist/src/commands/patch/staged-dependency-from.js +15 -9
  53. package/dist/src/commands/patch/staged-dependency-remove.d.ts +26 -4
  54. package/dist/src/commands/patch/staged-dependency-remove.js +60 -23
  55. package/dist/src/commands/patch/staged-dependency.js +35 -24
  56. package/dist/src/commands/patch/tier.js +2 -2
  57. package/dist/src/commands/patch-rename-map.d.ts +14 -3
  58. package/dist/src/commands/patch-rename-map.js +21 -3
  59. package/dist/src/commands/re-export-adjacent.d.ts +4 -5
  60. package/dist/src/commands/re-export-lint.d.ts +4 -3
  61. package/dist/src/commands/re-export-lint.js +4 -3
  62. package/dist/src/commands/re-export-options.js +8 -2
  63. package/dist/src/commands/re-export-preflight.js +1 -2
  64. package/dist/src/commands/re-export-purity-scope.d.ts +1 -1
  65. package/dist/src/commands/re-export-purity-scope.js +13 -7
  66. package/dist/src/commands/re-export-retired-declarations.d.ts +22 -0
  67. package/dist/src/commands/re-export-retired-declarations.js +113 -0
  68. package/dist/src/commands/re-export-single.js +2 -2
  69. package/dist/src/commands/re-export-stale-registrations.d.ts +27 -6
  70. package/dist/src/commands/re-export-stale-registrations.js +65 -21
  71. package/dist/src/commands/re-export.js +4 -0
  72. package/dist/src/commands/rebase/continue.js +3 -3
  73. package/dist/src/commands/register.js +5 -4
  74. package/dist/src/commands/run.js +11 -4
  75. package/dist/src/commands/status.js +1 -1
  76. package/dist/src/commands/test-build-furnace.d.ts +1 -2
  77. package/dist/src/commands/test-build-furnace.js +1 -2
  78. package/dist/src/commands/test-build-phase.d.ts +8 -0
  79. package/dist/src/commands/test-build-phase.js +42 -7
  80. package/dist/src/commands/test-control-restore.d.ts +40 -0
  81. package/dist/src/commands/test-control-restore.js +86 -0
  82. package/dist/src/commands/test-dispatch.js +68 -7
  83. package/dist/src/commands/test-multi-verdict.js +1 -0
  84. package/dist/src/commands/test-orchestrate.d.ts +6 -0
  85. package/dist/src/commands/test-orchestrate.js +32 -1
  86. package/dist/src/commands/test-perf-scope.d.ts +1 -1
  87. package/dist/src/commands/test-perf-scope.js +1 -1
  88. package/dist/src/commands/test-prelock.d.ts +18 -8
  89. package/dist/src/commands/test-prelock.js +42 -14
  90. package/dist/src/commands/test-register.js +4 -3
  91. package/dist/src/commands/test-results.d.ts +10 -1
  92. package/dist/src/commands/test-results.js +20 -3
  93. package/dist/src/commands/test-run.d.ts +6 -0
  94. package/dist/src/commands/test-run.js +23 -4
  95. package/dist/src/commands/test-shards.d.ts +7 -3
  96. package/dist/src/commands/test-shards.js +24 -7
  97. package/dist/src/commands/test-stale-gate.d.ts +6 -1
  98. package/dist/src/commands/test-stale-gate.js +40 -2
  99. package/dist/src/commands/test-verdict.d.ts +5 -3
  100. package/dist/src/commands/test.js +14 -2
  101. package/dist/src/commands/token.js +2 -2
  102. package/dist/src/commands/verify.js +1 -1
  103. package/dist/src/commands/watch.js +1 -1
  104. package/dist/src/commands/wire.js +1 -1
  105. package/dist/src/core/build-audit-resolve.d.ts +4 -5
  106. package/dist/src/core/build-audit-resolve.js +4 -5
  107. package/dist/src/core/build-audit-skip.d.ts +2 -2
  108. package/dist/src/core/build-audit.d.ts +3 -4
  109. package/dist/src/core/build-baseline-types.d.ts +24 -3
  110. package/dist/src/core/build-baseline-validate.d.ts +30 -1
  111. package/dist/src/core/build-baseline-validate.js +127 -35
  112. package/dist/src/core/build-baseline.d.ts +8 -2
  113. package/dist/src/core/build-baseline.js +25 -16
  114. package/dist/src/core/build-jar-escalation.d.ts +4 -4
  115. package/dist/src/core/build-jar-escalation.js +4 -4
  116. package/dist/src/core/build-prepare.js +4 -4
  117. package/dist/src/core/config-validate.js +4 -4
  118. package/dist/src/core/coverage-extend.d.ts +5 -3
  119. package/dist/src/core/coverage-extend.js +8 -3
  120. package/dist/src/core/coverage-records.d.ts +4 -5
  121. package/dist/src/core/coverage-records.js +4 -5
  122. package/dist/src/core/destructive.d.ts +2 -2
  123. package/dist/src/core/destructive.js +2 -2
  124. package/dist/src/core/discard-baseline.d.ts +2 -2
  125. package/dist/src/core/discard-baseline.js +3 -3
  126. package/dist/src/core/engine-session-lock.d.ts +1 -1
  127. package/dist/src/core/engine-session-lock.js +1 -1
  128. package/dist/src/core/file-lock-owner.d.ts +5 -5
  129. package/dist/src/core/file-lock-owner.js +5 -5
  130. package/dist/src/core/file-lock-queue.js +6 -8
  131. package/dist/src/core/file-lock-shared.d.ts +4 -4
  132. package/dist/src/core/file-lock-shared.js +4 -4
  133. package/dist/src/core/file-lock.js +7 -10
  134. package/dist/src/core/firefox.js +2 -2
  135. package/dist/src/core/furnace-apply-overwrite-warn.d.ts +1 -1
  136. package/dist/src/core/furnace-apply-overwrite-warn.js +1 -1
  137. package/dist/src/core/furnace-apply.js +3 -3
  138. package/dist/src/core/furnace-operation.js +5 -5
  139. package/dist/src/core/furnace-registration-jar-inplace.js +5 -6
  140. package/dist/src/core/furnace-registration-rename.d.ts +5 -5
  141. package/dist/src/core/furnace-registration-rename.js +5 -5
  142. package/dist/src/core/furnace-registration-validate.d.ts +2 -1
  143. package/dist/src/core/furnace-registration-validate.js +2 -1
  144. package/dist/src/core/furnace-registration.js +2 -2
  145. package/dist/src/core/git-base.js +4 -3
  146. package/dist/src/core/git-diff.js +1 -1
  147. package/dist/src/core/git.js +1 -1
  148. package/dist/src/core/harness-orphans.d.ts +6 -6
  149. package/dist/src/core/harness-orphans.js +6 -6
  150. package/dist/src/core/harness-port-reservation.d.ts +60 -0
  151. package/dist/src/core/harness-port-reservation.js +122 -0
  152. package/dist/src/core/history-log-file.d.ts +9 -9
  153. package/dist/src/core/history-log-file.js +15 -15
  154. package/dist/src/core/history-log.d.ts +3 -3
  155. package/dist/src/core/history-log.js +3 -3
  156. package/dist/src/core/launchable-build.d.ts +39 -0
  157. package/dist/src/core/launchable-build.js +85 -0
  158. package/dist/src/core/marionette-port.js +1 -3
  159. package/dist/src/core/moz-manifest-rules.js +3 -2
  160. package/dist/src/core/negative-control-compiled.d.ts +13 -3
  161. package/dist/src/core/negative-control-compiled.js +27 -5
  162. package/dist/src/core/negative-control-diff.js +31 -6
  163. package/dist/src/core/parser-fallback.d.ts +5 -5
  164. package/dist/src/core/parser-fallback.js +5 -5
  165. package/dist/src/core/patch-identifier-suggest.d.ts +6 -0
  166. package/dist/src/core/patch-identifier-suggest.js +11 -4
  167. package/dist/src/core/patch-lint-checkjs.d.ts +1 -1
  168. package/dist/src/core/patch-lint-checkjs.js +1 -1
  169. package/dist/src/core/patch-lint-css.js +2 -2
  170. package/dist/src/core/patch-lint-file-size.d.ts +5 -7
  171. package/dist/src/core/patch-lint-file-size.js +5 -7
  172. package/dist/src/core/patch-lint-ignore.d.ts +3 -3
  173. package/dist/src/core/patch-lint-ignore.js +3 -3
  174. package/dist/src/core/patch-lint-prettier.d.ts +4 -6
  175. package/dist/src/core/patch-lint-prettier.js +4 -6
  176. package/dist/src/core/patch-lint-projection.d.ts +1 -1
  177. package/dist/src/core/patch-lint-projection.js +1 -1
  178. package/dist/src/core/patch-lint-queue-types.d.ts +2 -2
  179. package/dist/src/core/patch-lint-queue-types.js +2 -2
  180. package/dist/src/core/patch-manifest-consistency.d.ts +2 -2
  181. package/dist/src/core/patch-manifest-consistency.js +2 -2
  182. package/dist/src/core/patch-manifest-io.d.ts +5 -1
  183. package/dist/src/core/patch-manifest-io.js +12 -10
  184. package/dist/src/core/patch-manifest-key-order.d.ts +3 -4
  185. package/dist/src/core/patch-manifest-key-order.js +3 -4
  186. package/dist/src/core/patch-parse.d.ts +3 -3
  187. package/dist/src/core/patch-parse.js +3 -3
  188. package/dist/src/core/patch-policy.d.ts +1 -1
  189. package/dist/src/core/patch-policy.js +1 -1
  190. package/dist/src/core/patch-source-metadata.d.ts +1 -1
  191. package/dist/src/core/patch-source-metadata.js +1 -1
  192. package/dist/src/core/patch-subset.d.ts +5 -3
  193. package/dist/src/core/patch-subset.js +8 -7
  194. package/dist/src/core/patch-transform.d.ts +2 -2
  195. package/dist/src/core/patch-transform.js +2 -2
  196. package/dist/src/core/rebase-session.js +4 -6
  197. package/dist/src/core/register-xpcshell-test.d.ts +28 -7
  198. package/dist/src/core/register-xpcshell-test.js +76 -27
  199. package/dist/src/core/run-log.d.ts +8 -8
  200. package/dist/src/core/run-log.js +8 -8
  201. package/dist/src/core/status-classify.d.ts +1 -2
  202. package/dist/src/core/status-classify.js +1 -2
  203. package/dist/src/core/test-harness-crash.d.ts +5 -3
  204. package/dist/src/core/test-harness-crash.js +13 -14
  205. package/dist/src/core/test-harness-infra.d.ts +32 -0
  206. package/dist/src/core/test-harness-infra.js +69 -0
  207. package/dist/src/core/test-harness-signature.d.ts +6 -0
  208. package/dist/src/core/test-stale-check.d.ts +38 -0
  209. package/dist/src/core/test-stale-check.js +82 -1
  210. package/dist/src/core/test-stall-triage.js +5 -7
  211. package/dist/src/core/token-category.js +2 -2
  212. package/dist/src/core/token-inventory.js +3 -3
  213. package/dist/src/core/token-manager.d.ts +2 -2
  214. package/dist/src/core/token-manager.js +5 -7
  215. package/dist/src/core/token-variant.d.ts +6 -6
  216. package/dist/src/core/token-variant.js +7 -7
  217. package/dist/src/core/typecheck-shim.js +1 -1
  218. package/dist/src/core/wire-destroy.d.ts +1 -1
  219. package/dist/src/core/wire-destroy.js +1 -1
  220. package/dist/src/core/wire-dom-fragment.js +1 -1
  221. package/dist/src/core/wire-init.d.ts +1 -1
  222. package/dist/src/core/wire-init.js +1 -1
  223. package/dist/src/core/wire-subscript.d.ts +1 -1
  224. package/dist/src/core/wire-subscript.js +1 -1
  225. package/dist/src/core/wire-utils.d.ts +2 -2
  226. package/dist/src/core/wire-utils.js +3 -3
  227. package/dist/src/errors/base.d.ts +1 -1
  228. package/dist/src/errors/base.js +1 -1
  229. package/dist/src/errors/rebase.d.ts +1 -1
  230. package/dist/src/errors/rebase.js +1 -1
  231. package/dist/src/types/commands/options.d.ts +38 -3
  232. package/dist/src/types/config.d.ts +5 -7
  233. package/dist/src/utils/options.js +2 -2
  234. package/dist/src/utils/process.js +2 -2
  235. package/dist/src/utils/regex.d.ts +2 -2
  236. package/dist/src/utils/regex.js +3 -3
  237. package/dist/src/utils/sleep.d.ts +5 -6
  238. package/dist/src/utils/sleep.js +5 -6
  239. package/dist/src/utils/validation.js +2 -2
  240. package/docs/configuration.md +28 -23
  241. package/docs/exit-codes.md +8 -11
  242. package/docs/furnace.md +3 -3
  243. package/docs/lifecycle-invariants.md +36 -45
  244. package/docs/machine-output.md +41 -24
  245. package/docs/patch-workflow.md +140 -29
  246. package/docs/run-logs.md +8 -9
  247. package/docs/testing.md +114 -48
  248. package/package.json +1 -1
@@ -11,9 +11,9 @@ import { info, intro, outro } from '../../utils/logger.js';
11
11
  import { addWaitLockOption, resolveWaitLockSeconds } from '../../utils/options.js';
12
12
  import { requirePatchQueue, requirePatchTarget } from './patch-context.js';
13
13
  import { stagedDependencyDelta } from './staged-dependency-batch.js';
14
- import { applyMode, countStagedEntries, dependencyLabel, importView, registrationView, } from './staged-dependency-entries.js';
14
+ import { applyMode, countReasonUpdates, countStagedEntries, dependencyLabel, importView, registrationView, } from './staged-dependency-entries.js';
15
15
  import { runBatchAdd } from './staged-dependency-from.js';
16
- import { selectRemovalEntries } from './staged-dependency-remove.js';
16
+ import { declarationNoun, inferRemovalKinds, selectRemovalEntries, } from './staged-dependency-remove.js';
17
17
  import { validateStagedDependencyAdd } from './staged-dependency-validate.js';
18
18
  /** Joins the names of the missing flags for error attribution. */
19
19
  function missingFlagList(pairs) {
@@ -89,20 +89,25 @@ function requireRegistrationOptions(options) {
89
89
  /**
90
90
  * Renders a one-line summary of a staged-dependency metadata change.
91
91
  */
92
- function describeStagedDependencyChange(beforeCount, afterCount, mode, kind, targets) {
93
- const noun = kind === 'import' ? 'staged forward-import' : 'staged registration';
92
+ function describeStagedDependencyChange(before, after, mode, kinds, targets) {
93
+ const beforeCount = countStagedEntries(before);
94
+ const afterCount = countStagedEntries(after);
95
+ const targetKinds = [...new Set(targets.map((target) => target.kind))];
96
+ const noun = declarationNoun(targetKinds.length > 0 ? targetKinds : kinds);
94
97
  if (mode === 'clear') {
95
98
  return beforeCount === 0
96
99
  ? 'stagedDependencies was already empty — no change'
97
100
  : `cleared ${beforeCount} staged dependency declaration(s)`;
98
101
  }
99
- const label = targets.map((target) => dependencyLabel(kind, target)).join('; ');
102
+ const label = targets.map(({ kind, view }) => dependencyLabel(kind, view)).join('; ');
100
103
  if (targets.length === 0)
101
104
  return 'stagedDependencies unchanged';
102
105
  if (mode === 'add') {
103
- return afterCount === beforeCount
104
- ? `${noun} already present: ${label}`
105
- : `added ${noun}: ${label}`;
106
+ if (afterCount !== beforeCount)
107
+ return `added ${noun}: ${label}`;
108
+ return countReasonUpdates(before, after) > 0
109
+ ? `updated 1 ${noun} reason in place: ${label}`
110
+ : `${noun} already present: ${label}`;
106
111
  }
107
112
  return afterCount === beforeCount
108
113
  ? `no ${noun} matched: ${label}`
@@ -123,7 +128,11 @@ export async function patchStagedDependencyCommand(projectRoot, identifier, opti
123
128
  await runBatchAdd(projectRoot, identifier, mode, options);
124
129
  return;
125
130
  }
126
- const kind = kindFromOptions(options);
131
+ // Without --kind, --remove searches the kind its matcher names, or both.
132
+ const kinds = (mode === 'remove' ? inferRemovalKinds(options) : undefined) ?? [
133
+ kindFromOptions(options),
134
+ ];
135
+ const kind = kinds.length === 1 ? kinds[0] : undefined;
127
136
  // The patch queue loads before the declaration fields resolve so
128
137
  // `--remove` can infer `--creates` from the target patch's staged
129
138
  // entries.
@@ -132,31 +141,32 @@ export async function patchStagedDependencyCommand(projectRoot, identifier, opti
132
141
  const importDependency = mode === 'add' && kind === 'import' ? requireForwardImportOptions(options) : undefined;
133
142
  const registrationDependency = mode === 'add' && kind === 'registration' ? requireRegistrationOptions(options) : undefined;
134
143
  // Shape-check --add declarations against the loaded queue: a
135
- // patch-name-shaped --creates/--file is refused now instead of surfacing
144
+ // patch-name-shaped --creates/--file is refused here instead of surfacing
136
145
  // later as an undischargeable staged-dependency-unused.
137
146
  const added = importDependency ?? registrationDependency;
138
147
  if (added !== undefined) {
139
148
  validateStagedDependencyAdd(added, manifest.patches);
140
149
  }
141
150
  const targets = mode === 'remove'
142
- ? selectRemovalEntries(kind, options, targetPatch.stagedDependencies)
151
+ ? selectRemovalEntries(kinds, options, targetPatch.stagedDependencies)
143
152
  : importDependency !== undefined
144
- ? [importView(importDependency)]
153
+ ? [{ kind: 'import', view: importView(importDependency) }]
145
154
  : registrationDependency !== undefined
146
- ? [registrationView(registrationDependency)]
155
+ ? [{ kind: 'registration', view: registrationView(registrationDependency) }]
147
156
  : [];
148
- const applyKind = (entries, own, toView, addition) => {
149
- if (!own)
157
+ const applyKind = (entries, entryKind, toView, addition) => {
158
+ if (!kinds.includes(entryKind))
150
159
  return [...entries];
160
+ const own = targets.filter((target) => target.kind === entryKind).map(({ view }) => view);
151
161
  if (mode !== 'remove')
152
- return applyMode(entries, mode, targets[0], toView, addition);
153
- return targets.reduce((remaining, target) => applyMode(remaining, 'remove', target, toView, undefined), [...entries]);
162
+ return applyMode(entries, mode, own[0], toView, addition);
163
+ return own.reduce((remaining, target) => applyMode(remaining, 'remove', target, toView, undefined), [...entries]);
154
164
  };
155
165
  const applyToStaged = (staged) => {
156
166
  if (mode === 'clear')
157
167
  return {};
158
- const forwardImports = applyKind(staged?.forwardImports ?? [], kind === 'import', importView, importDependency);
159
- const registrations = applyKind(staged?.registrations ?? [], kind === 'registration', registrationView, registrationDependency);
168
+ const forwardImports = applyKind(staged?.forwardImports ?? [], 'import', importView, importDependency);
169
+ const registrations = applyKind(staged?.registrations ?? [], 'registration', registrationView, registrationDependency);
160
170
  return {
161
171
  ...(forwardImports.length > 0 ? { forwardImports } : {}),
162
172
  ...(registrations.length > 0 ? { registrations } : {}),
@@ -164,7 +174,7 @@ export async function patchStagedDependencyCommand(projectRoot, identifier, opti
164
174
  };
165
175
  const existing = targetPatch.stagedDependencies;
166
176
  const projected = applyToStaged(existing);
167
- const summary = describeStagedDependencyChange(countStagedEntries(existing), countStagedEntries(projected), mode, kind, targets);
177
+ const summary = describeStagedDependencyChange(existing, projected, mode, kinds, targets);
168
178
  if (isDryRun) {
169
179
  info(`[dry-run] ${targetPatch.filename}: ${summary}.`);
170
180
  outro('Dry run complete — no changes made');
@@ -184,7 +194,7 @@ export async function patchStagedDependencyCommand(projectRoot, identifier, opti
184
194
  }
185
195
  const before = result.before.stagedDependencies;
186
196
  const after = result.after.stagedDependencies;
187
- info(`${targetPatch.filename}: ${describeStagedDependencyChange(countStagedEntries(before), countStagedEntries(after), mode, kind, targets)}.`);
197
+ info(`${targetPatch.filename}: ${describeStagedDependencyChange(before, after, mode, kinds, targets)}.`);
188
198
  if (mode === 'remove' && countStagedEntries(after) === countStagedEntries(before)) {
189
199
  // The selection matched when the queue was read; a peer removed the
190
200
  // entries before the locked write. Still a miss, so still a refusal.
@@ -197,7 +207,8 @@ export async function patchStagedDependencyCommand(projectRoot, identifier, opti
197
207
  args: {
198
208
  filename: targetPatch.filename,
199
209
  mode,
200
- kind,
210
+ // A kind-less --remove searched both kinds; the delta names which.
211
+ ...(kind !== undefined ? { kind } : {}),
201
212
  ...stagedDependencyDelta(before, after),
202
213
  },
203
214
  ...(options.yes === true ? { yes: true } : {}),
@@ -219,13 +230,13 @@ export function registerPatchStagedDependency(parent, context) {
219
230
  .option('--add', 'Add a staged dependency declaration')
220
231
  .option('--remove', 'Remove matching staged dependency declaration(s); a selection that matches nothing exits 8')
221
232
  .option('--clear', 'Drop the stagedDependencies field entirely')
222
- .option('--kind <kind>', 'Declaration shape: "import" (forward import, the default) or "registration" (jar.mn packaging line, customElements or actor registration)')
233
+ .option('--kind <kind>', 'Declaration shape: "import" (forward import, the default) or "registration" (jar.mn packaging line, customElements or actor registration); --remove without it takes the kind --specifier or --line names, or both kinds')
223
234
  .option('--file <path>', 'Declaring file path relative to engine/')
224
235
  .option('--specifier <specifier>', 'Exact import specifier as it appears in source (--kind import)')
225
236
  .option('--line <text>', 'Registration/packaging line as the patch adds it, compared whitespace-insensitively (--kind registration)')
226
237
  .option('--creates <path>', 'Later-created file path relative to engine/ (with --remove and no --file: removes every declaration for that path)')
227
238
  .option('--owner <patch>', 'Exact later patch filename expected to create --creates')
228
- .option('--reason <text>', 'Human-readable rationale stored with the declaration (with --from: the default for entries without one)')
239
+ .option('--reason <text>', 'Human-readable rationale stored with the declaration; --add on a declaration already present replaces its reason in place (with --from: the default for entries without one)')
229
240
  .option('--from <json-file>', 'Add every declaration in a JSON file under one lock hold (--add only): a "lint --per-patch --report" ' +
230
241
  "document (its findings' stagedDeclarations for this patch) or an array of " +
231
242
  '{ file, specifier | line, creates, owner?, reason?, patch? } entries')
@@ -63,8 +63,8 @@ export async function patchTierCommand(projectRoot, identifier, options = {}) {
63
63
  : `change tier from "${before}" to "${after}"`;
64
64
  // Destructive-operation contract (docs/lifecycle-invariants.md): manifest
65
65
  // metadata mutations get summary + dry-run + confirmation/--yes + history
66
- // uniformly. This command used to accept --yes without ever prompting, so
67
- // the flag only appeared in the history record.
66
+ // uniformly, so --yes skips a real prompt rather than only appearing in
67
+ // the history record.
68
68
  const decision = await confirmDestructive({
69
69
  operation: 'patch-tier',
70
70
  title: `Patch tier: ${target.filename}`,
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * Structural equality for patch rename maps, shared by the placement-mode
3
3
  * export preview/commit check (`export-flow.ts`) and `patch reorder`'s
4
- * under-lock drift check. Both previously carried a byte-identical copy of
5
- * the sorted-entries comparison. One helper keeps the two commit gates from
6
- * drifting.
4
+ * under-lock drift check. One helper for the sorted-entries comparison
5
+ * keeps the two commit gates from drifting.
7
6
  */
8
7
  import type { PatchRenameEntry } from '../core/patch-manifest.js';
9
8
  /**
@@ -17,3 +16,15 @@ export declare function getSortedRenameEntries(renameMap: Map<string, PatchRenam
17
16
  * triple matches positionally.
18
17
  */
19
18
  export declare function renameMapsEqual(left: Map<string, PatchRenameEntry>, right: Map<string, PatchRenameEntry>): boolean;
19
+ /**
20
+ * Writes a committed rename map as one JSON object, old filename to new
21
+ * filename in the queue's new order (`{}` when nothing was renumbered), so
22
+ * a session can rewrite its references to the renumbered patches from the
23
+ * file instead of from the printed summary. Used by `patch reorder
24
+ * --map-out` and `patch split --map-out`.
25
+ *
26
+ * @param path - Output path, relative to the working directory
27
+ * @param renameMap - The committed rename map
28
+ * @returns The absolute path written
29
+ */
30
+ export declare function writeRenameMapFile(path: string, renameMap: Map<string, PatchRenameEntry>): Promise<string>;
@@ -2,10 +2,11 @@
2
2
  /**
3
3
  * Structural equality for patch rename maps, shared by the placement-mode
4
4
  * export preview/commit check (`export-flow.ts`) and `patch reorder`'s
5
- * under-lock drift check. Both previously carried a byte-identical copy of
6
- * the sorted-entries comparison. One helper keeps the two commit gates from
7
- * drifting.
5
+ * under-lock drift check. One helper for the sorted-entries comparison
6
+ * keeps the two commit gates from drifting.
8
7
  */
8
+ import { dirname, resolve } from 'node:path';
9
+ import { ensureDir, writeJson } from '../utils/fs.js';
9
10
  /**
10
11
  * Rename-map entries ordered by target order, so previews, history entries
11
12
  * and equality checks all walk the queue in its final on-disk order.
@@ -34,3 +35,20 @@ export function renameMapsEqual(left, right) {
34
35
  leftEntry.newOrder === rightEntry.newOrder);
35
36
  });
36
37
  }
38
+ /**
39
+ * Writes a committed rename map as one JSON object, old filename to new
40
+ * filename in the queue's new order (`{}` when nothing was renumbered), so
41
+ * a session can rewrite its references to the renumbered patches from the
42
+ * file instead of from the printed summary. Used by `patch reorder
43
+ * --map-out` and `patch split --map-out`.
44
+ *
45
+ * @param path - Output path, relative to the working directory
46
+ * @param renameMap - The committed rename map
47
+ * @returns The absolute path written
48
+ */
49
+ export async function writeRenameMapFile(path, renameMap) {
50
+ const absolute = resolve(path);
51
+ await ensureDir(dirname(absolute));
52
+ await writeJson(absolute, Object.fromEntries(getSortedRenameEntries(renameMap).map(([from, entry]) => [from, entry.newFilename])));
53
+ return absolute;
54
+ }
@@ -62,11 +62,10 @@ export declare function findMissingFiles(engineDir: string, files: readonly stri
62
62
  /**
63
63
  * One unmanaged file and the owned directory that made it adjacent.
64
64
  *
65
- * The anchor is what an unattended run could not previously recover: the
66
- * notice named the offending file and the patch, but not which of the
67
- * patch's owned locations the file sits beside, so triaging a refusal on a
68
- * patch that owns files in several directories meant re-deriving the
69
- * adjacency by hand from the manifest.
65
+ * The anchor names which of the patch's owned locations the file sits
66
+ * beside, so an unattended run can triage a refusal on a patch that owns
67
+ * files in several directories without re-deriving the adjacency by hand
68
+ * from the manifest.
70
69
  */
71
70
  export interface AdjacentUnmanagedFile {
72
71
  /** Engine-relative path of the unmanaged file. */
@@ -108,9 +108,10 @@ export declare function storeReExportLintResult(lintCtx: ReExportLintContext, pa
108
108
  * when the projection introduces error-severity cross-patch findings the
109
109
  * current queue does not have (a forward import an earlier owner gained,
110
110
  * say). Mirrors `re-export --files` (its projected-state conflict) and the
111
- * `--scan` adoption gate, which only covered files a scan adopted: a plain
112
- * re-export of an unchanged file set with new CONTENT used to pass, and the
113
- * queue carried the error until the next `export` placement refused it.
111
+ * `--scan` adoption gate, which only covers files a scan adopted: without
112
+ * this a plain re-export of an unchanged file set with new CONTENT would
113
+ * pass, and the queue would carry the error until the next `export`
114
+ * placement refused it.
114
115
  *
115
116
  * Runs in dry-run. `--force-unsafe` downgrades the refusal to warnings;
116
117
  * `--skip-lint` does not bypass it, same as `--files`. The baseline is the
@@ -221,9 +221,10 @@ export async function storeReExportLintResult(lintCtx, patchFilename, projectedM
221
221
  * when the projection introduces error-severity cross-patch findings the
222
222
  * current queue does not have (a forward import an earlier owner gained,
223
223
  * say). Mirrors `re-export --files` (its projected-state conflict) and the
224
- * `--scan` adoption gate, which only covered files a scan adopted: a plain
225
- * re-export of an unchanged file set with new CONTENT used to pass, and the
226
- * queue carried the error until the next `export` placement refused it.
224
+ * `--scan` adoption gate, which only covers files a scan adopted: without
225
+ * this a plain re-export of an unchanged file set with new CONTENT would
226
+ * pass, and the queue would carry the error until the next `export`
227
+ * placement refused it.
227
228
  *
228
229
  * Runs in dry-run. `--force-unsafe` downgrades the refusal to warnings;
229
230
  * `--skip-lint` does not bypass it, same as `--files`. The baseline is the
@@ -97,10 +97,16 @@ function validatePathFlagValues(options) {
97
97
  }
98
98
  }
99
99
  }
100
+ /**
101
+ * A retirement names its owner among explicitly selected patches. It
102
+ * combines with `--scan`: the retired path leaves the scan's base first,
103
+ * and the scan cannot adopt it back, since a retired created file is absent
104
+ * and a retired upstream file is clean at HEAD.
105
+ */
100
106
  function validateExpectedRemovalUsage(patches, options) {
101
107
  if (options.expectRemoved?.length &&
102
- (options.scan || options.all || options.files !== undefined || patches.length === 0)) {
103
- throw new InvalidArgumentError('--expect-removed requires explicit selected patches and the scan-less path (no --scan, --files, or --all).', '--expect-removed');
108
+ (options.all || options.files !== undefined || patches.length === 0)) {
109
+ throw new InvalidArgumentError('--expect-removed requires explicit selected patches and cannot combine with --files or --all.', '--expect-removed');
104
110
  }
105
111
  }
106
112
  /**
@@ -6,8 +6,7 @@
6
6
  * Under contention the lock wait runs to many minutes, and an argument the
7
7
  * run was always going to refuse (an empty patch name from an unquoted
8
8
  * `"$@"`, a changed-file path passed as a positional, a path flag that
9
- * swallowed the next flag) used to be reported only after that wait. These
10
- * checks read the manifest and a path-scoped `git status` without the lock.
9
+ * swallowed the next flag) is reported before that wait: these checks read the manifest and a path-scoped `git status` without the lock.
11
10
  * The command repeats its own resolution under the lock, so a manifest that
12
11
  * changes in between is still read fresh there.
13
12
  */
@@ -13,6 +13,6 @@ export declare function fingerprintScopedDryRunState(engineDir: string, patchesD
13
13
  /**
14
14
  * Names whoever else wrote during the dry run: history records from other
15
15
  * invocations since it started, and a live holder of the engine-session
16
- * lock (which a dry run no longer takes). Empty when there is no evidence.
16
+ * lock (which a dry run does not take). Empty when there is no evidence.
17
17
  */
18
18
  export declare function describeForeignWriters(patchesDir: string, sinceMs: number): Promise<string[]>;
@@ -2,10 +2,9 @@
2
2
  /**
3
3
  * The scoped half of the `re-export --dry-run` purity guard.
4
4
  *
5
- * A dry run takes no engine-session lock since 0.51.0, so peers keep
6
- * writing while it runs. The whole-tree fingerprint then reported every
7
- * peer's write as "invariant violated … This is a FireForge bug", and an
8
- * immediate repeat was clean. The scoped fingerprint covers only what the
5
+ * A dry run takes no engine-session lock, so peers keep writing while it
6
+ * runs. A whole-tree fingerprint would report every peer's write as
7
+ * "invariant violated … This is a FireForge bug". The scoped fingerprint covers only what the
9
8
  * dry run could touch: the selected patches' bodies and manifest rows, the
10
9
  * engine files they own, the engine HEAD and the engine index. A mismatch
11
10
  * that remains is attributed to a foreign writer when the history log or
@@ -17,6 +16,7 @@ import { readEngineSessionLockStatus } from '../core/engine-session-lock.js';
17
16
  import { git } from '../core/git-base.js';
18
17
  import { historyRecordIdentity, resolveHistoryLogPath } from '../core/history-log-file.js';
19
18
  import { PATCHES_MANIFEST } from '../core/patch-manifest-io.js';
19
+ import { canonicalizePatchRow } from '../core/patch-manifest-key-order.js';
20
20
  import { GeneralError } from '../errors/base.js';
21
21
  import { getNodeErrorCode, toError } from '../utils/errors.js';
22
22
  import { sha256Hex } from '../utils/hash.js';
@@ -33,7 +33,11 @@ async function hashOrAbsent(path, label) {
33
33
  throw new GeneralError(`[dry-run] cannot fingerprint ${label} for the purity guard: ${toError(error).message}`, error);
34
34
  }
35
35
  }
36
- /** The selected rows of `patches.json`, so a peer's write to another row is not a violation. */
36
+ /**
37
+ * The selected rows of `patches.json`, so a peer's write to another row is
38
+ * not a violation. Rows are hashed in canonical key order: a peer's write
39
+ * that only canonicalizes a selected row's key order changes no content.
40
+ */
37
41
  async function selectedRowsFingerprint(patchesDir, filenames) {
38
42
  let raw;
39
43
  try {
@@ -47,7 +51,9 @@ async function selectedRowsFingerprint(patchesDir, filenames) {
47
51
  try {
48
52
  const parsed = JSON.parse(raw);
49
53
  const rows = isObject(parsed) && isArray(parsed['patches']) ? parsed['patches'] : [];
50
- const selected = rows.filter((row) => isObject(row) && typeof row['filename'] === 'string' && filenames.has(row['filename']));
54
+ const selected = rows
55
+ .filter((row) => isObject(row) && typeof row['filename'] === 'string' && filenames.has(row['filename']))
56
+ .map((row) => (isObject(row) ? canonicalizePatchRow(row) : row));
51
57
  return sha256Hex(JSON.stringify(selected));
52
58
  }
53
59
  catch {
@@ -112,7 +118,7 @@ async function foreignHistoryRecords(projectRoot, sinceMs) {
112
118
  /**
113
119
  * Names whoever else wrote during the dry run: history records from other
114
120
  * invocations since it started, and a live holder of the engine-session
115
- * lock (which a dry run no longer takes). Empty when there is no evidence.
121
+ * lock (which a dry run does not take). Empty when there is no evidence.
116
122
  */
117
123
  export async function describeForeignWriters(patchesDir, sinceMs) {
118
124
  const projectRoot = dirname(resolve(patchesDir));
@@ -0,0 +1,22 @@
1
+ import type { PatchesManifest, PatchMetadata, ReExportOptions } from '../types/commands/index.js';
2
+ import type { ProjectPaths } from '../types/config.js';
3
+ /** What {@link dropRetiredPathDeclarations} needs from the run. */
4
+ export interface RetiredPathDeclarationsArgs {
5
+ /** Resolved project paths. */
6
+ paths: ProjectPaths;
7
+ /** The queue as the run loaded it (judged in a dry run). */
8
+ manifest: PatchesManifest;
9
+ /** The run's options; `expectRemoved` holds the validated paths. */
10
+ options: ReExportOptions;
11
+ /** The run's selected patches. */
12
+ selectedPatches: readonly PatchMetadata[];
13
+ /** Name the drops without writing. */
14
+ isDryRun: boolean;
15
+ }
16
+ /**
17
+ * Drops (or, in a dry run, names) every staged declaration whose `creates`
18
+ * path this run retired. Runs after the selected patches are written, so a
19
+ * path whose owner failed to re-export is still owned and its declarations
20
+ * stay.
21
+ */
22
+ export declare function dropRetiredPathDeclarations(args: RetiredPathDeclarationsArgs): Promise<void>;
@@ -0,0 +1,113 @@
1
+ // SPDX-License-Identifier: EUPL-1.2
2
+ /**
3
+ * `re-export --expect-removed` drops the staged declarations whose
4
+ * `creates` path the run retires.
5
+ *
6
+ * Retiring a file would otherwise leave another patch's staged
7
+ * registration (or forward import) for it in place, unflagged until the
8
+ * per-patch lint after both re-exports reported `staged-dependency-unused`.
9
+ * The run that retires the path drops every such declaration, in any patch of the queue, in one manifest
10
+ * update, and names each one.
11
+ */
12
+ import { withPatchDirectoryLock } from '../core/patch-lock.js';
13
+ import { loadPatchesManifestForWrite, mutatePatchRowsInManifest } from '../core/patch-manifest.js';
14
+ import { info } from '../utils/logger.js';
15
+ import { recordStagedDeclarationPrune, } from './re-export-stale-registrations.js';
16
+ /**
17
+ * Finds the declarations whose `creates` path is retired and owned by no
18
+ * patch outside `retiring`.
19
+ *
20
+ * @param manifest - The queue to judge
21
+ * @param removed - The run's `--expect-removed` paths
22
+ * @param retiring - Patches whose ownership of a removed path does not
23
+ * count: the selected owners in a dry run, whose rows are not rewritten
24
+ * yet; nothing after the write, where a failed owner still owns its path
25
+ */
26
+ function planRetiredDeclarationDrops(manifest, removed, retiring) {
27
+ const orphaned = new Set(removed.filter((file) => !manifest.patches.some((patch) => !retiring.has(patch.filename) && patch.filesAffected.includes(file))));
28
+ if (orphaned.size === 0)
29
+ return [];
30
+ const drops = [];
31
+ for (const patch of manifest.patches) {
32
+ const drop = planHolderDrop(patch, orphaned);
33
+ if (drop !== undefined)
34
+ drops.push(drop);
35
+ }
36
+ return drops;
37
+ }
38
+ function planHolderDrop(patch, orphaned) {
39
+ const forwardImports = patch.stagedDependencies?.forwardImports ?? [];
40
+ const registrations = patch.stagedDependencies?.registrations ?? [];
41
+ const dropped = [
42
+ ...registrations
43
+ .filter((entry) => orphaned.has(entry.creates))
44
+ .map((entry) => ({ kind: 'registration', ...entry })),
45
+ ...forwardImports
46
+ .filter((entry) => orphaned.has(entry.creates))
47
+ .map((entry) => ({ kind: 'import', ...entry })),
48
+ ];
49
+ if (dropped.length === 0)
50
+ return undefined;
51
+ const keptImports = forwardImports.filter((entry) => !orphaned.has(entry.creates));
52
+ const keptRegistrations = registrations.filter((entry) => !orphaned.has(entry.creates));
53
+ const staged = {
54
+ ...(keptImports.length > 0 ? { forwardImports: keptImports } : {}),
55
+ ...(keptRegistrations.length > 0 ? { registrations: keptRegistrations } : {}),
56
+ };
57
+ return {
58
+ holder: patch.filename,
59
+ dropped,
60
+ stagedDependencies: Object.keys(staged).length > 0 ? staged : undefined,
61
+ };
62
+ }
63
+ function reportRetiredDeclarationDrops(drops, isDryRun) {
64
+ const verb = isDryRun ? 'would drop' : 'drops';
65
+ for (const { holder, dropped } of drops) {
66
+ for (const entry of dropped) {
67
+ const what = entry.kind === 'registration'
68
+ ? `staged registration ${entry.file} "${entry.line.trim()}"`
69
+ : `staged forward-import ${entry.file} "${entry.specifier}"`;
70
+ info(`${holder}: ${verb} ${what}: --expect-removed retires ${entry.creates}.`);
71
+ }
72
+ }
73
+ }
74
+ /**
75
+ * Drops (or, in a dry run, names) every staged declaration whose `creates`
76
+ * path this run retired. Runs after the selected patches are written, so a
77
+ * path whose owner failed to re-export is still owned and its declarations
78
+ * stay.
79
+ */
80
+ export async function dropRetiredPathDeclarations(args) {
81
+ const { manifest, isDryRun } = args;
82
+ const patchesDir = args.paths.patches;
83
+ const removed = args.options.expectRemoved ?? [];
84
+ const selected = new Set(args.selectedPatches.map((patch) => patch.filename));
85
+ if (removed.length === 0)
86
+ return;
87
+ if (isDryRun) {
88
+ reportRetiredDeclarationDrops(planRetiredDeclarationDrops(manifest, removed, selected), isDryRun);
89
+ return;
90
+ }
91
+ const drops = await withPatchDirectoryLock(patchesDir, async () => {
92
+ const current = await loadPatchesManifestForWrite(patchesDir);
93
+ if (!current)
94
+ return [];
95
+ const planned = planRetiredDeclarationDrops(current, removed, new Set());
96
+ const byHolder = new Map(planned.map((drop) => [drop.holder, drop]));
97
+ if (byHolder.size === 0)
98
+ return [];
99
+ await mutatePatchRowsInManifest(patchesDir, [...byHolder.keys()], (existing) => {
100
+ const drop = byHolder.get(existing.filename);
101
+ if (drop === undefined)
102
+ return null;
103
+ return drop.stagedDependencies === undefined
104
+ ? { unset: ['stagedDependencies'] }
105
+ : { set: { stagedDependencies: drop.stagedDependencies } };
106
+ });
107
+ return planned;
108
+ }, { command: 're-export' });
109
+ reportRetiredDeclarationDrops(drops, isDryRun);
110
+ for (const drop of drops) {
111
+ await recordStagedDeclarationPrune(patchesDir, drop.holder, drop.dropped, 'staged declarations on retired paths');
112
+ }
113
+ }
@@ -23,7 +23,7 @@ import { reportForeignDrift } from './re-export-drift.js';
23
23
  import { buildHunkSelectedDiff, hunkSelectionOf } from './re-export-hunks.js';
24
24
  import { assertRefreshedBodyHasNoNewQueueErrors, lintReExportedPatch, refreshQueueCtxEntry, storeReExportLintResult, } from './re-export-lint.js';
25
25
  import { assertScanAdoptionsHaveNoForwardImports, assertScanFileAdditionsHaveDiffHunks, confirmBroadScanAdditions, scanPatchFilesForReExport, } from './re-export-scan.js';
26
- import { planStaleRegistrationDrop, recordStaleRegistrationDrop, reportStaleRegistrationDrop, } from './re-export-stale-registrations.js';
26
+ import { findPendingRegistrationCreates, planStaleRegistrationDrop, recordStaleRegistrationDrop, reportStaleRegistrationDrop, } from './re-export-stale-registrations.js';
27
27
  const GIT_INDEX_LOCK_RETRY_MS = 300;
28
28
  /**
29
29
  * Retries a single patch re-export exactly once when the failure is
@@ -211,7 +211,7 @@ async function reExportSinglePatch(args) {
211
211
  return false;
212
212
  }
213
213
  const { updates, ignoreChecks, effectiveTier, existingIgnoreSet, flagIgnoreSet } = computeReExportUpdates(patch, options, currentFilesAffected);
214
- const stalePlan = planStaleRegistrationDrop(manifest, patch, currentFilesAffected);
214
+ const stalePlan = planStaleRegistrationDrop(manifest, patch, currentFilesAffected, await findPendingRegistrationCreates(paths.engine, manifest, patch, currentFilesAffected, adjacentCtx.expectedNew));
215
215
  if (stalePlan !== undefined)
216
216
  reportStaleRegistrationDrop(patch.filename, stalePlan, isDryRun);
217
217
  const unset = applyStaleRegistrationDrop(stalePlan, updates);
@@ -1,18 +1,39 @@
1
- import type { PatchesManifest, PatchMetadata, PatchStagedDependencies, PatchStagedRegistration } from '../types/commands/index.js';
1
+ import type { PatchesManifest, PatchMetadata, PatchStagedDependencies, PatchStagedForwardImport, PatchStagedRegistration } from '../types/commands/index.js';
2
2
  /** The holder's registrations split by whether the queue still owns `creates`. */
3
3
  export interface StaleRegistrationPlan {
4
4
  /** Registrations whose `creates` path no patch owns. */
5
5
  dropped: PatchStagedRegistration[];
6
+ /** Unowned registrations kept because their `creates` file is pending adoption. */
7
+ pending: PatchStagedRegistration[];
6
8
  /** The staged block to write, or undefined when nothing is left. */
7
9
  stagedDependencies: PatchStagedDependencies | undefined;
8
10
  }
11
+ /**
12
+ * The unowned `creates` paths of `patch`'s registrations that are pending
13
+ * adoption: the file exists in `engine/`, or is an `--expect-new` path of
14
+ * this run. Such a registration is waiting for its subject patch's scan.
15
+ */
16
+ export declare function findPendingRegistrationCreates(engineDir: string, manifest: PatchesManifest, patch: PatchMetadata, currentFilesAffected: readonly string[], expectedNew: ReadonlySet<string>): Promise<Set<string>>;
9
17
  /**
10
18
  * Finds `patch`'s registrations whose `creates` path no patch in the queue
11
- * owns, judging `patch` by the file list this re-export will write.
12
- * Returns undefined when nothing is stale.
19
+ * owns, judging `patch` by the file list this re-export will write. A
20
+ * registration whose `creates` path is in `pending` (see
21
+ * {@link findPendingRegistrationCreates}) is kept and reported instead.
22
+ * Returns undefined when nothing is stale or pending.
13
23
  */
14
- export declare function planStaleRegistrationDrop(manifest: PatchesManifest, patch: PatchMetadata, currentFilesAffected: readonly string[]): StaleRegistrationPlan | undefined;
15
- /** Names each dropped registration, in both a dry run and a write. */
24
+ export declare function planStaleRegistrationDrop(manifest: PatchesManifest, patch: PatchMetadata, currentFilesAffected: readonly string[], pendingCreates?: ReadonlySet<string>): StaleRegistrationPlan | undefined;
25
+ /** Names each dropped and each kept-pending registration, in both a dry run and a write. */
16
26
  export declare function reportStaleRegistrationDrop(patchFilename: string, plan: StaleRegistrationPlan, isDryRun: boolean): void;
17
- /** Records the dropped declarations as a delta, like `staged-dependency --remove`. */
27
+ /** One declaration a re-export pruned, as the history log records it. */
28
+ export type PrunedStagedDeclaration = ({
29
+ kind: 'registration';
30
+ } & PatchStagedRegistration) | ({
31
+ kind: 'import';
32
+ } & PatchStagedForwardImport);
33
+ /**
34
+ * Records declarations a re-export pruned from one patch as a delta, like
35
+ * `staged-dependency --remove`.
36
+ */
37
+ export declare function recordStagedDeclarationPrune(patchesDir: string, patchFilename: string, removed: readonly PrunedStagedDeclaration[], what: string): Promise<void>;
38
+ /** Records the dropped registrations as a delta, like `staged-dependency --remove`. */
18
39
  export declare function recordStaleRegistrationDrop(patchesDir: string, patchFilename: string, plan: StaleRegistrationPlan): Promise<void>;