@detergent-software/atk 9.1.0 → 9.2.0-dev.10

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 (305) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/README.md +37 -17
  3. package/build/command-manifest.json +89 -7
  4. package/build/commands/_app.d.ts +2 -1
  5. package/build/commands/_app.d.ts.map +1 -1
  6. package/build/commands/audit.d.ts +1 -1
  7. package/build/commands/audit.d.ts.map +1 -1
  8. package/build/commands/browse.d.ts +1 -1
  9. package/build/commands/browse.d.ts.map +1 -1
  10. package/build/commands/browse.js +119 -16
  11. package/build/commands/browse.js.map +1 -1
  12. package/build/commands/cache.d.ts +1 -1
  13. package/build/commands/cache.d.ts.map +1 -1
  14. package/build/commands/checkout.d.ts +1 -1
  15. package/build/commands/checkout.d.ts.map +1 -1
  16. package/build/commands/config.d.ts +1 -1
  17. package/build/commands/config.d.ts.map +1 -1
  18. package/build/commands/deprecate.d.ts +1 -1
  19. package/build/commands/deprecate.d.ts.map +1 -1
  20. package/build/commands/diff.d.ts +4 -2
  21. package/build/commands/diff.d.ts.map +1 -1
  22. package/build/commands/diff.js +86 -13
  23. package/build/commands/diff.js.map +1 -1
  24. package/build/commands/doctor.d.ts +1 -1
  25. package/build/commands/doctor.d.ts.map +1 -1
  26. package/build/commands/drift.d.ts +2 -1
  27. package/build/commands/drift.d.ts.map +1 -1
  28. package/build/commands/drift.js +8 -3
  29. package/build/commands/drift.js.map +1 -1
  30. package/build/commands/freeze.d.ts +2 -2
  31. package/build/commands/freeze.d.ts.map +1 -1
  32. package/build/commands/freeze.js +45 -22
  33. package/build/commands/freeze.js.map +1 -1
  34. package/build/commands/index.d.ts +1 -1
  35. package/build/commands/index.d.ts.map +1 -1
  36. package/build/commands/info.d.ts +1 -1
  37. package/build/commands/info.d.ts.map +1 -1
  38. package/build/commands/info.js +24 -4
  39. package/build/commands/info.js.map +1 -1
  40. package/build/commands/init.d.ts +1 -1
  41. package/build/commands/init.d.ts.map +1 -1
  42. package/build/commands/install.d.ts +2 -2
  43. package/build/commands/install.d.ts.map +1 -1
  44. package/build/commands/install.js +284 -66
  45. package/build/commands/install.js.map +1 -1
  46. package/build/commands/list.d.ts +2 -1
  47. package/build/commands/list.d.ts.map +1 -1
  48. package/build/commands/list.js +64 -6
  49. package/build/commands/list.js.map +1 -1
  50. package/build/commands/login.d.ts +1 -1
  51. package/build/commands/login.d.ts.map +1 -1
  52. package/build/commands/logout.d.ts +1 -1
  53. package/build/commands/logout.d.ts.map +1 -1
  54. package/build/commands/move.d.ts +1 -1
  55. package/build/commands/move.d.ts.map +1 -1
  56. package/build/commands/outdated.d.ts +2 -1
  57. package/build/commands/outdated.d.ts.map +1 -1
  58. package/build/commands/outdated.js +11 -1
  59. package/build/commands/outdated.js.map +1 -1
  60. package/build/commands/pin.d.ts +2 -2
  61. package/build/commands/pin.d.ts.map +1 -1
  62. package/build/commands/pin.js +77 -46
  63. package/build/commands/pin.js.map +1 -1
  64. package/build/commands/promote.d.ts +1 -1
  65. package/build/commands/promote.d.ts.map +1 -1
  66. package/build/commands/prune.d.ts +1 -1
  67. package/build/commands/prune.d.ts.map +1 -1
  68. package/build/commands/publish.d.ts +1 -1
  69. package/build/commands/publish.d.ts.map +1 -1
  70. package/build/commands/register.d.ts +1 -1
  71. package/build/commands/register.d.ts.map +1 -1
  72. package/build/commands/release-notes.d.ts +1 -1
  73. package/build/commands/release-notes.d.ts.map +1 -1
  74. package/build/commands/reviewer.d.ts +1 -1
  75. package/build/commands/reviewer.d.ts.map +1 -1
  76. package/build/commands/schema.d.ts +2 -2
  77. package/build/commands/schema.d.ts.map +1 -1
  78. package/build/commands/search.d.ts +2 -1
  79. package/build/commands/search.d.ts.map +1 -1
  80. package/build/commands/search.js +4 -2
  81. package/build/commands/search.js.map +1 -1
  82. package/build/commands/setup.d.ts +1 -1
  83. package/build/commands/setup.d.ts.map +1 -1
  84. package/build/commands/sync.d.ts +14 -2
  85. package/build/commands/sync.d.ts.map +1 -1
  86. package/build/commands/sync.js +86 -17
  87. package/build/commands/sync.js.map +1 -1
  88. package/build/commands/thaw.d.ts +2 -2
  89. package/build/commands/thaw.d.ts.map +1 -1
  90. package/build/commands/thaw.js +44 -21
  91. package/build/commands/thaw.js.map +1 -1
  92. package/build/commands/uninstall.d.ts +2 -2
  93. package/build/commands/uninstall.d.ts.map +1 -1
  94. package/build/commands/uninstall.js +35 -11
  95. package/build/commands/uninstall.js.map +1 -1
  96. package/build/commands/unpin.d.ts +2 -2
  97. package/build/commands/unpin.d.ts.map +1 -1
  98. package/build/commands/unpin.js +54 -25
  99. package/build/commands/unpin.js.map +1 -1
  100. package/build/commands/unpublish.d.ts +1 -1
  101. package/build/commands/unpublish.d.ts.map +1 -1
  102. package/build/commands/update.d.ts +3 -2
  103. package/build/commands/update.d.ts.map +1 -1
  104. package/build/commands/update.js +270 -74
  105. package/build/commands/update.js.map +1 -1
  106. package/build/commands/validate.d.ts +1 -1
  107. package/build/commands/validate.d.ts.map +1 -1
  108. package/build/commands/whoami.d.ts +1 -1
  109. package/build/commands/whoami.d.ts.map +1 -1
  110. package/build/commands/why.d.ts +1 -1
  111. package/build/commands/why.d.ts.map +1 -1
  112. package/build/components/AssetDetail.d.ts +6 -1
  113. package/build/components/AssetDetail.d.ts.map +1 -1
  114. package/build/components/AssetDetail.js +9 -2
  115. package/build/components/AssetDetail.js.map +1 -1
  116. package/build/components/AssetTable.d.ts +1 -1
  117. package/build/components/AssetTable.d.ts.map +1 -1
  118. package/build/components/AssetVersionLine.d.ts +1 -1
  119. package/build/components/AssetVersionLine.d.ts.map +1 -1
  120. package/build/components/BrowseExpandedBundle.d.ts +1 -1
  121. package/build/components/BrowseExpandedBundle.d.ts.map +1 -1
  122. package/build/components/BrowseExpandedRow.d.ts +1 -1
  123. package/build/components/BrowseExpandedRow.d.ts.map +1 -1
  124. package/build/components/BrowseList.d.ts +3 -1
  125. package/build/components/BrowseList.d.ts.map +1 -1
  126. package/build/components/BrowseList.js +12 -6
  127. package/build/components/BrowseList.js.map +1 -1
  128. package/build/components/BundleDetail.d.ts +3 -1
  129. package/build/components/BundleDetail.d.ts.map +1 -1
  130. package/build/components/BundleDetail.js +4 -2
  131. package/build/components/BundleDetail.js.map +1 -1
  132. package/build/components/CrossScopeWarning.d.ts +1 -1
  133. package/build/components/CrossScopeWarning.d.ts.map +1 -1
  134. package/build/components/DependencyTree.d.ts +1 -1
  135. package/build/components/DependencyTree.d.ts.map +1 -1
  136. package/build/components/DeprecationSummary.d.ts +2 -2
  137. package/build/components/DeprecationSummary.d.ts.map +1 -1
  138. package/build/components/DiagnosticList.d.ts +1 -1
  139. package/build/components/DiagnosticList.d.ts.map +1 -1
  140. package/build/components/DiffView.d.ts +34 -5
  141. package/build/components/DiffView.d.ts.map +1 -1
  142. package/build/components/DiffView.js +47 -8
  143. package/build/components/DiffView.js.map +1 -1
  144. package/build/components/DryRunBanner.d.ts +1 -1
  145. package/build/components/DryRunBanner.d.ts.map +1 -1
  146. package/build/components/ErrorView.d.ts +2 -2
  147. package/build/components/ErrorView.d.ts.map +1 -1
  148. package/build/components/ExcludedSkippedHint.d.ts +1 -1
  149. package/build/components/ExcludedSkippedHint.d.ts.map +1 -1
  150. package/build/components/Field.d.ts +1 -1
  151. package/build/components/Field.d.ts.map +1 -1
  152. package/build/components/Field.js +1 -1
  153. package/build/components/Field.js.map +1 -1
  154. package/build/components/FilterBar.d.ts +1 -1
  155. package/build/components/FilterBar.d.ts.map +1 -1
  156. package/build/components/FrozenSkippedHint.d.ts +1 -1
  157. package/build/components/FrozenSkippedHint.d.ts.map +1 -1
  158. package/build/components/Header.d.ts +1 -1
  159. package/build/components/Header.d.ts.map +1 -1
  160. package/build/components/HelpBar.d.ts +5 -1
  161. package/build/components/HelpBar.d.ts.map +1 -1
  162. package/build/components/HelpBar.js +18 -4
  163. package/build/components/HelpBar.js.map +1 -1
  164. package/build/components/InitSuccess.d.ts +1 -1
  165. package/build/components/InitSuccess.d.ts.map +1 -1
  166. package/build/components/InstallConfirmBar.d.ts +5 -3
  167. package/build/components/InstallConfirmBar.d.ts.map +1 -1
  168. package/build/components/InstallConfirmBar.js +15 -2
  169. package/build/components/InstallConfirmBar.js.map +1 -1
  170. package/build/components/InstallSummary.d.ts +1 -1
  171. package/build/components/InstallSummary.d.ts.map +1 -1
  172. package/build/components/InstalledTree.d.ts +17 -0
  173. package/build/components/InstalledTree.d.ts.map +1 -0
  174. package/build/components/InstalledTree.js +31 -0
  175. package/build/components/InstalledTree.js.map +1 -0
  176. package/build/components/Link.d.ts +1 -1
  177. package/build/components/Link.d.ts.map +1 -1
  178. package/build/components/ListActionBar.d.ts +1 -1
  179. package/build/components/ListActionBar.d.ts.map +1 -1
  180. package/build/components/ListBrowseList.d.ts +1 -1
  181. package/build/components/ListBrowseList.d.ts.map +1 -1
  182. package/build/components/ListExpandedRow.d.ts +1 -1
  183. package/build/components/ListExpandedRow.d.ts.map +1 -1
  184. package/build/components/ListHelpBar.d.ts +1 -1
  185. package/build/components/ListHelpBar.d.ts.map +1 -1
  186. package/build/components/LocalEditsHint.d.ts +1 -1
  187. package/build/components/LocalEditsHint.d.ts.map +1 -1
  188. package/build/components/MultiSelectList.d.ts +1 -1
  189. package/build/components/MultiSelectList.d.ts.map +1 -1
  190. package/build/components/NoticeBanner.d.ts +1 -1
  191. package/build/components/NoticeBanner.d.ts.map +1 -1
  192. package/build/components/OrphanListItem.d.ts +1 -1
  193. package/build/components/OrphanListItem.d.ts.map +1 -1
  194. package/build/components/PromptField.d.ts +1 -1
  195. package/build/components/PromptField.d.ts.map +1 -1
  196. package/build/components/ReadmeSection.d.ts +1 -1
  197. package/build/components/ReadmeSection.d.ts.map +1 -1
  198. package/build/components/RegistryChangeSummary.d.ts +2 -2
  199. package/build/components/RegistryChangeSummary.d.ts.map +1 -1
  200. package/build/components/RunStoppedHint.d.ts +1 -1
  201. package/build/components/RunStoppedHint.d.ts.map +1 -1
  202. package/build/components/SchemaTable.d.ts +1 -1
  203. package/build/components/SchemaTable.d.ts.map +1 -1
  204. package/build/components/SectionDivider.d.ts +1 -1
  205. package/build/components/SectionDivider.d.ts.map +1 -1
  206. package/build/components/SkippedAssetSection.d.ts +1 -1
  207. package/build/components/SkippedAssetSection.d.ts.map +1 -1
  208. package/build/components/Spinner.d.ts +1 -1
  209. package/build/components/Spinner.d.ts.map +1 -1
  210. package/build/components/StatusBadge.d.ts +2 -2
  211. package/build/components/StatusBadge.d.ts.map +1 -1
  212. package/build/components/SyncAllInSync.d.ts +1 -1
  213. package/build/components/SyncAllInSync.d.ts.map +1 -1
  214. package/build/components/SyncDriftedList.d.ts +1 -1
  215. package/build/components/SyncDriftedList.d.ts.map +1 -1
  216. package/build/components/SyncNoAssets.d.ts +1 -1
  217. package/build/components/SyncNoAssets.d.ts.map +1 -1
  218. package/build/components/UndeclaredRemovedHint.d.ts +1 -1
  219. package/build/components/UndeclaredRemovedHint.d.ts.map +1 -1
  220. package/build/components/UpdateBanner.d.ts +1 -1
  221. package/build/components/UpdateBanner.d.ts.map +1 -1
  222. package/build/components/index.d.ts +2 -1
  223. package/build/components/index.d.ts.map +1 -1
  224. package/build/components/index.js +2 -1
  225. package/build/components/index.js.map +1 -1
  226. package/build/hooks/useBrowseState.d.ts +31 -1
  227. package/build/hooks/useBrowseState.d.ts.map +1 -1
  228. package/build/hooks/useBrowseState.js +99 -15
  229. package/build/hooks/useBrowseState.js.map +1 -1
  230. package/build/hooks/useUninstallState.d.ts +22 -2
  231. package/build/hooks/useUninstallState.d.ts.map +1 -1
  232. package/build/hooks/useUninstallState.js +161 -16
  233. package/build/hooks/useUninstallState.js.map +1 -1
  234. package/build/lib/atk-api/generated/types.gen.d.ts +8 -8
  235. package/build/lib/atk-schemas/claude-plugin.d.ts +56 -0
  236. package/build/lib/atk-schemas/claude-plugin.d.ts.map +1 -0
  237. package/build/lib/atk-schemas/claude-plugin.js +199 -0
  238. package/build/lib/atk-schemas/claude-plugin.js.map +1 -0
  239. package/build/lib/atk-schemas/index.d.ts +3 -0
  240. package/build/lib/atk-schemas/index.d.ts.map +1 -1
  241. package/build/lib/atk-schemas/index.js +3 -0
  242. package/build/lib/atk-schemas/index.js.map +1 -1
  243. package/build/lib/atk-schemas/manifest.d.ts +2 -2
  244. package/build/lib/atk-schemas/manifest.d.ts.map +1 -1
  245. package/build/lib/atk-schemas/null-optionals.d.ts +1 -1
  246. package/build/lib/atk-schemas/null-optionals.d.ts.map +1 -1
  247. package/build/lib/atk-schemas/registry.d.ts +6 -0
  248. package/build/lib/atk-schemas/registry.d.ts.map +1 -1
  249. package/build/lib/atk-schemas/registry.js +2 -0
  250. package/build/lib/atk-schemas/registry.js.map +1 -1
  251. package/build/lib/config.d.ts +18 -0
  252. package/build/lib/config.d.ts.map +1 -1
  253. package/build/lib/config.js +32 -9
  254. package/build/lib/config.js.map +1 -1
  255. package/build/lib/diff-command.d.ts +43 -3
  256. package/build/lib/diff-command.d.ts.map +1 -1
  257. package/build/lib/diff-command.js +93 -15
  258. package/build/lib/diff-command.js.map +1 -1
  259. package/build/lib/format.d.ts +5 -0
  260. package/build/lib/format.d.ts.map +1 -1
  261. package/build/lib/format.js +7 -0
  262. package/build/lib/format.js.map +1 -1
  263. package/build/lib/install-targets.d.ts +90 -0
  264. package/build/lib/install-targets.d.ts.map +1 -0
  265. package/build/lib/install-targets.js +146 -0
  266. package/build/lib/install-targets.js.map +1 -0
  267. package/build/lib/installed-tree.d.ts +47 -0
  268. package/build/lib/installed-tree.d.ts.map +1 -0
  269. package/build/lib/installed-tree.js +72 -0
  270. package/build/lib/installed-tree.js.map +1 -0
  271. package/build/lib/installer.js +3 -2
  272. package/build/lib/installer.js.map +1 -1
  273. package/build/lib/named-entry.d.ts +24 -0
  274. package/build/lib/named-entry.d.ts.map +1 -0
  275. package/build/lib/named-entry.js +33 -0
  276. package/build/lib/named-entry.js.map +1 -0
  277. package/build/lib/pastel/_app.d.ts +1 -1
  278. package/build/lib/pastel/_app.d.ts.map +1 -1
  279. package/build/lib/registry.d.ts +43 -0
  280. package/build/lib/registry.d.ts.map +1 -1
  281. package/build/lib/registry.js +73 -0
  282. package/build/lib/registry.js.map +1 -1
  283. package/build/lib/resolver.d.ts +35 -0
  284. package/build/lib/resolver.d.ts.map +1 -1
  285. package/build/lib/resolver.js +65 -21
  286. package/build/lib/resolver.js.map +1 -1
  287. package/build/lib/schemas/config.d.ts +2 -2
  288. package/build/lib/schemas/config.d.ts.map +1 -1
  289. package/build/lib/search.d.ts +9 -2
  290. package/build/lib/search.d.ts.map +1 -1
  291. package/build/lib/search.js +18 -1
  292. package/build/lib/search.js.map +1 -1
  293. package/build/lib/sync.d.ts +9 -0
  294. package/build/lib/sync.d.ts.map +1 -1
  295. package/build/lib/sync.js +13 -8
  296. package/build/lib/sync.js.map +1 -1
  297. package/build/lib/uninstaller.d.ts +51 -3
  298. package/build/lib/uninstaller.d.ts.map +1 -1
  299. package/build/lib/uninstaller.js +134 -39
  300. package/build/lib/uninstaller.js.map +1 -1
  301. package/build/lib/updater.d.ts +6 -7
  302. package/build/lib/updater.d.ts.map +1 -1
  303. package/build/lib/updater.js +23 -33
  304. package/build/lib/updater.js.map +1 -1
  305. package/package.json +21 -21
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Box, Text, useApp } from 'ink';
3
- import { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { Fragment, useCallback, useEffect, useRef, useState } from 'react';
4
4
  import { z } from 'zod';
5
5
  import { CrossScopeWarning, DryRunBanner, ErrorView, InstallSummary, MultiSelectList, Spinner, } from '../components/index.js';
6
6
  import { useCommand } from '../hooks/useCommand.js';
@@ -10,7 +10,9 @@ import { resolveBundleVersion } from '../lib/bundle-version.js';
10
10
  import { loadConfig } from '../lib/config.js';
11
11
  import { crossScopeNotice, findCrossScopeInstalls } from '../lib/cross-scope.js';
12
12
  import { errorGuidance, errorMessage } from '../lib/errors.js';
13
+ import { bundleMembers, installPlannedTargets, isBundleInPlace, planTargetsInstall } from '../lib/install-targets.js';
13
14
  import { isInstalledForTool } from '../lib/installed-for-tool.js';
15
+ import { InstallerError } from '../lib/installer.js';
14
16
  import { addAssetToLockfile, findInstalledAsset, identityKey, readLockfile, upgradeToDirectInstall, withLockfileErrorHandling, writeLockfile, } from '../lib/lockfile.js';
15
17
  import { executeInstallPlanForTools, withoutKeptDependencies } from '../lib/multi-tool-install.js';
16
18
  import { addNotice } from '../lib/notices.js';
@@ -36,7 +38,7 @@ export const options = z.object({
36
38
  .default(false)
37
39
  .describe(option({
38
40
  alias: 'a',
39
- description: 'Install every asset from an org scope without prompting (for `atk install @org`; required with --json or no terminal)',
41
+ description: 'Install every asset from an org scope without prompting (for `atk install @org`; required with --json, no terminal, or other names)',
40
42
  })),
41
43
  dryRun: z
42
44
  .boolean()
@@ -108,7 +110,8 @@ export const options = z.object({
108
110
  description: 'Alias for --all: install every asset from an org scope without prompting',
109
111
  })),
110
112
  });
111
- export const args = z.tuple([
113
+ export const args = z
114
+ .tuple([
112
115
  z
113
116
  .string()
114
117
  .optional()
@@ -116,12 +119,19 @@ export const args = z.tuple([
116
119
  description: 'Asset, bundle, or org scope to install (supports name@version and @org)',
117
120
  name: 'target',
118
121
  })),
119
- ]);
120
- export default function Install({ args: [rawName], options: { adopt, all, dryRun, force, json, org, pin, projectRoot, refresh, tool, user, yes }, }) {
122
+ ])
123
+ .rest(z.string().describe(argument({
124
+ description: 'More assets, bundles or org scopes to install with the first, as one plan (name@version, @org/name, @org with --all)',
125
+ name: 'targets',
126
+ })));
127
+ export default function Install({ args: [rawName, ...more], options: { adopt, all, dryRun, force, json, org, pin, projectRoot, refresh, tool, user, yes }, }) {
121
128
  // No argument provided — delegate to sync behavior
122
129
  if (!rawName) {
123
130
  return _jsx(Sync, { options: { dryRun, force, json, projectRoot, refresh, tool, user } });
124
131
  }
132
+ if (more.length > 0) {
133
+ return (_jsx(InstallAssets, { adopt: adopt, all: all || yes, dryRun: dryRun, force: force, json: json, org: org, pin: pin, projectRoot: projectRoot, specs: [rawName, ...more], tool: tool, user: user }));
134
+ }
125
135
  if (isBareOrgScope(rawName)) {
126
136
  const orgName = rawName.slice(1);
127
137
  return (_jsx(InstallOrg, { cliOrg: org, dryRun: dryRun, force: force, json: json, orgName: orgName, pin: pin, projectRoot: projectRoot, skipPicker: all || yes, tool: tool, user: user }));
@@ -141,20 +151,6 @@ async function bundleAtRequestedVersion(entry, requested, progress) {
141
151
  }
142
152
  return resolution.bundle;
143
153
  }
144
- /**
145
- * The bundle's members that one of `tools` places, as the registry has them. A member none of them
146
- * places is left out, since this run can't adopt it, and so is a ref the registry can't find, which
147
- * the install reports.
148
- */
149
- function bundleMembers(registry, bundle, tools) {
150
- return (bundle.assets ?? []).flatMap((ref) => {
151
- const found = findBundleMember(registry, bundle, ref);
152
- if (!found || !tools.some((t) => placesAsset(t.adapter, found.asset.type, found.versionData.tools)))
153
- return [];
154
- const { name, org, type } = found.asset;
155
- return [{ name, ...(org ? { org } : {}), type }];
156
- });
157
- }
158
154
  /**
159
155
  * What a bundle install says about its members outside the bundle (`kept`) and the members it
160
156
  * `adopted`. A member with no origin can be adopted; another bundle's member stays with that bundle.
@@ -215,15 +211,6 @@ async function crossScopeOf(plan, { installedAt, json, scope, tools }) {
215
211
  return {};
216
212
  }
217
213
  }
218
- /**
219
- * The registry asset a bundle's ref names, on the same (name, type, org) identity the resolver's
220
- * install plan uses: a same-named asset in another scope is not this ref. An unqualified ref falls
221
- * back to the bundle's own org, never the installer's active org.
222
- */
223
- function findBundleMember(registry, bundle, ref) {
224
- const found = findAsset(registry, ref.name, ref.type, ref.version, ref.org ?? bundle.org);
225
- return found && (!ref.org || found.asset.org === ref.org) ? found : undefined;
226
- }
227
214
  /** The same org install with `--all`, for a run that can't show the picker. */
228
215
  function installAllCommand({ orgName, ...flags }) {
229
216
  return installCommandFor(`@${orgName} --all`, flags);
@@ -274,16 +261,8 @@ function InstallAsset({ adopt, dryRun, force, json, org: cliOrg, pin, projectRoo
274
261
  // through to a misleading "asset not found".
275
262
  if (!indexBundle && (registry.bundles ?? []).some((b) => b.name === name)) {
276
263
  const assetExists = findAsset(registry, name, undefined, undefined, org);
277
- if (!assetExists) {
278
- if (inlineOrg) {
279
- throw new Error(`Bundle '${name}' not found in org '${inlineOrg}'.`);
280
- }
281
- const orgScopes = (registry.bundles ?? [])
282
- .filter((b) => b.name === name && b.org)
283
- .map((b) => `@${b.org}/${name}`);
284
- throw new Error(`Bundle '${name}' not found in the registry.` +
285
- (orgScopes.length > 0 ? ` Did you mean ${orgScopes.join(' or ')}?` : ''));
286
- }
264
+ if (!assetExists)
265
+ throw otherScopeBundleError(registry, name, inlineOrg);
287
266
  }
288
267
  // With no active org, `info` opens the only org asset of a bare name, but install doesn't
289
268
  // switch scopes on its own: it names the spec that installs that asset.
@@ -361,26 +340,7 @@ function InstallAsset({ adopt, dryRun, force, json, org: cliOrg, pin, projectRoo
361
340
  };
362
341
  // Bundle installation flow — check if already installed for ALL tools
363
342
  if (!force) {
364
- const bundleAssets = bundle.assets ?? [];
365
- // When no configured tool takes any member, fall through so the installer says why.
366
- const allInstalledForAllTools = tools.every(({ adapter: toolAdapter }) => members.length > 0 &&
367
- bundleAssets.every((ref) => {
368
- const found = findBundleMember(registry, bundle, ref);
369
- if (!found)
370
- return false;
371
- // A tool that doesn't take this member (its type, or a hook for other tools) has
372
- // nothing to check.
373
- if (!placesAsset(toolAdapter, found.asset.type, found.versionData.tools))
374
- return true;
375
- const installed = findInstalledAsset(before, ref.name, ref.type, found.asset.org);
376
- if (!installed)
377
- return false;
378
- if (requiredBundleVersion && !isFromBundleVersion(installed, bundle, requiredBundleVersion)) {
379
- return false;
380
- }
381
- return isInstalledForTool(installed, toolAdapter, projectDir);
382
- }));
383
- if (allInstalledForAllTools) {
343
+ if (isBundleInPlace(registry, bundle, before, { members, projectDir, requiredBundleVersion, tools })) {
384
344
  // Adopting a bundle that is in place only records its origin on the members with none:
385
345
  // their files, versions and pins stay as they are. Another bundle's member stays its.
386
346
  const outside = membersOutsideBundle(before, members, bundle);
@@ -511,16 +471,228 @@ function InstallAsset({ adopt, dryRun, force, json, org: cliOrg, pin, projectRoo
511
471
  return (_jsxs(Box, { flexDirection: 'column', children: [_jsxs(Text, { color: 'green', children: ["\u2713 ", _jsx(Text, { bold: true, children: data.name }), " (v", data.version, ") is now a direct install", where, "."] }), _jsx(Text, { dimColor: true, children: "It will no longer be auto-removed when its parent is uninstalled." })] }));
512
472
  }
513
473
  if (data.outcome === 'dry-run-install') {
514
- return (_jsxs(Box, { flexDirection: 'column', children: [_jsx(DryRunBanner, { action: 'install', count: data.plan.totalCount, tools: data.tools, where: label }), _jsx(Text, { children: " " }), data.plan.entries.map((entry) => (_jsxs(Text, { children: [' ', entry.reason === 'dependency' ? (_jsxs(Text, { color: 'cyan', children: [formatOrgName(entry.asset.org, entry.asset.name), ' ', _jsxs(Text, { dimColor: true, children: ["(", entry.asset.type, ") v", entry.version, " \u2014 dependency"] })] })) : (_jsxs(Text, { children: [_jsx(Text, { bold: true, children: formatOrgName(entry.asset.org, entry.asset.name) }), _jsxs(Text, { dimColor: true, children: [' ', "(", entry.asset.type, ") v", entry.version] })] }))] }, identityKey(entry.asset)))), _jsx(CrossScopeWarning, { installs: data.crossScope ?? [] })] }));
474
+ return (_jsxs(Box, { flexDirection: 'column', children: [_jsx(DryRunBanner, { action: 'install', count: data.plan.totalCount, tools: data.tools, where: label }), _jsx(Text, { children: " " }), _jsx(PlanEntryLines, { plan: data.plan }), _jsx(CrossScopeWarning, { installs: data.crossScope ?? [] })] }));
515
475
  }
516
476
  if (data.outcome === 'dry-run-bundle') {
517
- return (_jsxs(Box, { flexDirection: 'column', children: [_jsx(DryRunBanner, { action: 'install', count: data.plan.totalCount, tools: data.tools, where: label }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [_jsxs(Text, { bold: true, color: 'magenta', children: ["Bundle: ", data.bundleName] }), _jsxs(Text, { dimColor: true, children: [" v", data.version] })] }), _jsx(Text, { children: " " }), data.plan.entries.map((entry) => (_jsxs(Text, { children: [' ', entry.reason === 'dependency' ? (_jsxs(Text, { color: 'cyan', children: [formatOrgName(entry.asset.org, entry.asset.name), ' ', _jsxs(Text, { dimColor: true, children: ["(", entry.asset.type, ") v", entry.version, " \u2014 dependency"] })] })) : (_jsxs(Text, { children: [_jsx(Text, { bold: true, children: formatOrgName(entry.asset.org, entry.asset.name) }), _jsxs(Text, { dimColor: true, children: [' ', "(", entry.asset.type, ") v", entry.version] })] }))] }, identityKey(entry.asset)))), _jsx(BundleReportLines, { bundleName: data.bundleName, dryRun: true, report: data }), _jsx(CrossScopeWarning, { installs: data.crossScope ?? [] })] }));
477
+ return (_jsxs(Box, { flexDirection: 'column', children: [_jsx(DryRunBanner, { action: 'install', count: data.plan.totalCount, tools: data.tools, where: label }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [_jsxs(Text, { bold: true, color: 'magenta', children: ["Bundle: ", data.bundleName] }), _jsxs(Text, { dimColor: true, children: [" v", data.version] })] }), _jsx(Text, { children: " " }), _jsx(PlanEntryLines, { plan: data.plan }), _jsx(BundleReportLines, { bundleName: data.bundleName, dryRun: true, report: data }), _jsx(CrossScopeWarning, { installs: data.crossScope ?? [] })] }));
518
478
  }
519
479
  if (data.outcome === 'deprecated') {
520
480
  return (_jsxs(Box, { flexDirection: 'column', children: [_jsxs(Text, { color: 'yellow', children: ["Warning: \"", data.name, "\" is deprecated \u2014 ", data.reason] }), data.replacement && (_jsxs(Text, { children: ["Consider using", ' ', _jsx(Text, { bold: true, color: 'cyan', children: data.replacement }), ' ', "instead."] })), _jsx(Text, { dimColor: true, children: "Use --force to install anyway." })] }));
521
481
  }
522
482
  return (_jsxs(Box, { flexDirection: 'column', children: [data.toolResults.map(({ results, skipped, tool: toolName, warnings }) => (_jsx(InstallSummary, { results: results, skipped: skipped, tools: [toolName], warnings: warnings, where: label }, toolName))), _jsx(CrossScopeWarning, { installs: data.crossScope ?? [] })] }));
523
483
  }
484
+ /**
485
+ * `atk install a b@1.2.0 @org/c my-bundle @org --all`: the named assets, the bundles' members and
486
+ * the orgs' assets resolved together (shared dependencies once, one version per asset) and
487
+ * installed as one plan, so a failure rolls the whole run back, as an `@org` install does. Every
488
+ * name is checked before anything is written. An asset already in place for every tool is left out
489
+ * of the plan, and one installed only as a dependency becomes direct once the install succeeds. A
490
+ * bundle is left out when all of it is in place, as when it is installed alone; its members get
491
+ * its origin unless they are named themselves ({@link resolveTargetsInstallPlan}).
492
+ */
493
+ function InstallAssets({ adopt, all, dryRun, force, json, org: cliOrg, pin, projectRoot, specs, tool, user, }) {
494
+ const { json: effectiveJson } = useNonInteractive(json);
495
+ const { fields, resolve, resolved } = useResolvedScope({ projectRoot, user });
496
+ const state = useCommand(async (progress) => {
497
+ const flags = { dryRun, force, pin, projectRoot, tool, user };
498
+ if (adopt) {
499
+ throw new Error('--adopt records a bundle as the owner of its members, so it works with one bundle only.');
500
+ }
501
+ // No picker opens halfway through a run, so an org among names takes all its assets, and
502
+ // only when asked to. A dry run shows them all, as `atk install @org --dry-run` does.
503
+ const orgScopes = [...new Set(specs.filter(isBareOrgScope))];
504
+ if (orgScopes.length > 0 && !all && !dryRun) {
505
+ throw new InstallerError(`${orgScopes.join(', ')} among other names installs every asset in ` +
506
+ `${orgScopes.length === 1 ? 'it' : 'them'}, so it needs --all (-a). ` +
507
+ `To choose ${orgScopes.length === 1 ? 'its' : 'their'} assets, install ${orgScopes.length === 1 ? 'it' : 'each'} alone.`, { code: 'selection_required', command: installCommandFor(`${specs.join(' ')} --all`, flags) });
508
+ }
509
+ const { root: projectDir, scope: installScope } = await resolve();
510
+ progress('Loading configuration...');
511
+ const config = await loadConfig();
512
+ const tools = await resolvePlacingTools(tool, projectDir);
513
+ const toolNames = tools.map((t) => t.tool);
514
+ progress('Fetching registry...');
515
+ const registry = await fetchRegistry({ force: true });
516
+ const currentLockfile = await readLockfile(projectDir);
517
+ // Every name is checked before anything is resolved or written. The targets keep the order
518
+ // they were named in, which decides the origin of a member two bundles share.
519
+ const deprecated = [];
520
+ const emptyOrgs = [];
521
+ const orgs = [];
522
+ const targets = [];
523
+ const deprecationOf = (name, deprecation) => {
524
+ if (deprecation.deprecated && !force) {
525
+ deprecated.push(`"${name}" (${deprecation.reason}${deprecation.replacement ? `; use ${deprecation.replacement}` : ''})`);
526
+ }
527
+ };
528
+ for (const spec of new Set(specs)) {
529
+ if (isBareOrgScope(spec)) {
530
+ const orgName = spec.slice(1);
531
+ const orgAssets = getAssetsByOrg(registry, orgName);
532
+ if (orgAssets.length === 0)
533
+ emptyOrgs.push(spec);
534
+ orgs.push({ count: orgAssets.length, org: orgName });
535
+ for (const { name, type } of orgAssets) {
536
+ targets.push({
537
+ fromOrg: true,
538
+ kind: 'asset',
539
+ name,
540
+ org: orgName,
541
+ spec: formatOrgName(orgName, name),
542
+ type,
543
+ });
544
+ }
545
+ continue;
546
+ }
547
+ const { name: parsedName, version } = parseNameVersion(spec);
548
+ const { name: nameFromOrg, org: inlineOrg } = parseBundleSpec(parsedName);
549
+ const name = inlineOrg ? nameFromOrg : parsedName;
550
+ const org = resolveActiveOrg(inlineOrg ?? cliOrg, currentLockfile.org, config.org);
551
+ const indexBundle = findBundle(registry, name, inlineOrg);
552
+ if (indexBundle) {
553
+ const bundle = await bundleAtRequestedVersion(indexBundle, version, progress);
554
+ const earlier = targets.find((t) => t.kind === 'bundle' && t.bundle.name === name && t.bundle.org === indexBundle.org);
555
+ if (earlier) {
556
+ // A bundle installs at one version, as an asset does.
557
+ if (earlier.bundle.version !== bundle.version) {
558
+ throw new ResolverError(`Bundle "${formatOrgName(bundle.org, name)}" is named at two versions: ${earlier.bundle.version} ` +
559
+ `(${earlier.spec}) and ${bundle.version} (${spec}). A bundle installs at one version.`);
560
+ }
561
+ continue;
562
+ }
563
+ deprecationOf(name, isDeprecated(registry, name, undefined, indexBundle.org));
564
+ targets.push({ bundle, kind: 'bundle', spec, ...(version ? { version } : {}) });
565
+ continue;
566
+ }
567
+ const asset = findAsset(registry, name, undefined, undefined, org)?.asset;
568
+ if (!asset && (registry.bundles ?? []).some((b) => b.name === name)) {
569
+ throw otherScopeBundleError(registry, name, inlineOrg);
570
+ }
571
+ deprecationOf(name, isDeprecated(registry, name, asset?.type, asset ? asset.org : inlineOrg));
572
+ targets.push({ kind: 'asset', name, ...(org ? { org } : {}), spec, ...(version ? { version } : {}) });
573
+ }
574
+ if (emptyOrgs.length > 0) {
575
+ throw new ResolverError(`No assets found for ${emptyOrgs.join(', ')}. Nothing was installed.`);
576
+ }
577
+ if (deprecated.length > 0) {
578
+ throw new ResolverError(`Deprecated: ${deprecated.join(', ')}. Nothing was installed.`, {
579
+ hint: 'Use --force to install them anyway.',
580
+ });
581
+ }
582
+ // A name every targeted tool already has in place is left out of the plan, as it is for one name.
583
+ const adoptCommandFor = ({ bundle, version }) => installCommandFor(formatOrgName(bundle.org, bundle.name) + (version ? `@${version}` : ''), {
584
+ adopt: true,
585
+ force: false,
586
+ pin: false,
587
+ projectRoot,
588
+ tool,
589
+ user,
590
+ });
591
+ progress('Resolving dependencies...');
592
+ const targetsPlan = planTargetsInstall(registry, targets, currentLockfile, tools, { force, projectDir });
593
+ const { assets: report, plan, toInstall } = targetsPlan;
594
+ const bundles = targetsPlan.bundlesInPlace.map((request) => {
595
+ const { bundle } = request;
596
+ return {
597
+ name: bundle.name,
598
+ ...(bundle.org ? { org: bundle.org } : {}),
599
+ outcome: 'already-installed',
600
+ version: bundle.version,
601
+ ...bundleReport(membersOutsideBundle(currentLockfile, bundleMembers(registry, bundle, tools), bundle), [], {
602
+ adoptCommand: adoptCommandFor(request),
603
+ bundle,
604
+ }),
605
+ };
606
+ });
607
+ const crossScope = (resolvedPlan, installedAt) => crossScopeOf(resolvedPlan, { installedAt, json: effectiveJson, scope: installScope, tools: toolNames });
608
+ // Each bundle in the plan, in the order named, then the ones left out; and the orgs.
609
+ const targetsField = (lockfile, planned) => {
610
+ const installedBundles = toInstall.flatMap((request) => {
611
+ if (request.kind !== 'bundle')
612
+ return [];
613
+ const { bundle } = request;
614
+ const members = bundleMembers(registry, bundle, tools);
615
+ // A dry run writes nothing, so the members it leaves outside the bundle are read from the plan.
616
+ const kept = [
617
+ ...membersOutsideBundle(lockfile, members, bundle),
618
+ ...(planned ? plannedOutsideBundle(plan, lockfile, members, bundle) : []),
619
+ ];
620
+ return [
621
+ {
622
+ name: bundle.name,
623
+ ...(bundle.org ? { org: bundle.org } : {}),
624
+ outcome: 'installed',
625
+ version: bundle.version,
626
+ ...bundleReport(kept, [], { adoptCommand: adoptCommandFor(request), bundle }),
627
+ },
628
+ ];
629
+ });
630
+ const named = [...installedBundles, ...bundles];
631
+ return { ...(named.length > 0 ? { bundles: named } : {}), ...(orgs.length > 0 ? { orgs } : {}) };
632
+ };
633
+ // A dry run says so even when every name is in place, and writes nothing (not even an upgrade).
634
+ if (dryRun) {
635
+ return {
636
+ assets: report,
637
+ ...targetsField(currentLockfile, true),
638
+ outcome: 'dry-run-install',
639
+ plan: withoutKeptDependencies(plan, currentLockfile, tools, {
640
+ onProgress: progress,
641
+ projectDir,
642
+ scope: installScope,
643
+ }),
644
+ tools: toolNames,
645
+ ...(await crossScope(plan)),
646
+ };
647
+ }
648
+ return await withLockfileErrorHandling(projectDir, async () => {
649
+ const toolResults = await installPlannedTargets(targetsPlan, tools, {
650
+ force,
651
+ onProgress: progress,
652
+ onTool: (toolName) => progress(`Installing for ${toolName}...`),
653
+ pin,
654
+ projectDir,
655
+ scope: installScope,
656
+ });
657
+ if (plan.entries.length === 0) {
658
+ return { assets: report, ...targetsField(currentLockfile, false), outcome: 'already-installed' };
659
+ }
660
+ return {
661
+ assets: report,
662
+ ...targetsField(await readLockfile(projectDir), false),
663
+ outcome: 'installed',
664
+ toolResults,
665
+ ...(await crossScope(plan, projectDir)),
666
+ };
667
+ });
668
+ }, [adopt, all, cliOrg, dryRun, force, pin, specs.join(' '), tool]);
669
+ if (state.status === 'loading') {
670
+ return _jsx(Spinner, { message: state.message });
671
+ }
672
+ if (state.status === 'error') {
673
+ if (effectiveJson) {
674
+ return renderJson({
675
+ ...(state.code ? { code: state.code } : {}),
676
+ ...(state.command ? { command: state.command } : {}),
677
+ ...(state.hint ? { hint: state.hint } : {}),
678
+ message: state.message,
679
+ outcome: 'error',
680
+ ...fields,
681
+ });
682
+ }
683
+ return _jsx(ErrorView, { command: state.command, hint: state.hint, message: state.message });
684
+ }
685
+ const { data } = state;
686
+ const label = resolved && scopeLabel(resolved);
687
+ if (effectiveJson) {
688
+ return renderJson({ ...data, ...fields });
689
+ }
690
+ if (data.outcome === 'dry-run-install') {
691
+ return (_jsxs(Box, { flexDirection: 'column', children: [_jsx(DryRunBanner, { action: 'install', count: data.plan.totalCount, tools: data.tools, where: label }), _jsx(Text, { children: " " }), _jsx(PlanEntryLines, { plan: data.plan }), _jsx(NamedAssetLines, { assets: data.assets, dryRun: true, where: label }), _jsx(TargetLines, { bundles: data.bundles, dryRun: true, orgs: data.orgs, where: label }), _jsx(CrossScopeWarning, { installs: data.crossScope ?? [] })] }));
692
+ }
693
+ return (_jsxs(Box, { flexDirection: 'column', children: [data.outcome === 'installed' &&
694
+ data.toolResults.map(({ results, skipped, tool: toolName, warnings }) => (_jsx(InstallSummary, { results: results, skipped: skipped, tools: [toolName], warnings: warnings, where: label }, toolName))), _jsx(NamedAssetLines, { assets: data.assets, where: label }), _jsx(TargetLines, { bundles: data.bundles, orgs: data.orgs, where: label }), data.outcome === 'installed' && _jsx(CrossScopeWarning, { installs: data.crossScope ?? [] })] }));
695
+ }
524
696
  /** `atk install <spec>` with the run's own flags, for a command an error suggests. */
525
697
  function installCommandFor(spec, { adopt, dryRun, force, pin, projectRoot, tool, user }) {
526
698
  return [
@@ -767,7 +939,7 @@ function InstallOrg({ cliOrg, dryRun, force, json, orgName, pin, projectRoot, sk
767
939
  ...fields,
768
940
  });
769
941
  }
770
- return (_jsxs(Box, { flexDirection: 'column', children: [_jsx(DryRunBanner, { action: 'install', count: state.plan.totalCount, tools: state.tools, where: label }), _jsx(Text, { children: " " }), _jsx(Text, { children: _jsxs(Text, { bold: true, color: 'magenta', children: ["Org: @", orgName] }) }), _jsx(Text, { children: " " }), state.plan.entries.map((entry) => (_jsxs(Text, { children: [' ', entry.reason === 'dependency' ? (_jsxs(Text, { color: 'cyan', children: [formatOrgName(entry.asset.org, entry.asset.name), ' ', _jsxs(Text, { dimColor: true, children: ["(", entry.asset.type, ") v", entry.version, " \u2014 dependency"] })] })) : (_jsxs(Text, { children: [_jsx(Text, { bold: true, children: formatOrgName(entry.asset.org, entry.asset.name) }), _jsxs(Text, { dimColor: true, children: [' ', "(", entry.asset.type, ") v", entry.version] })] }))] }, identityKey(entry.asset)))), _jsx(CrossScopeWarning, { installs: state.crossScope ?? [] })] }));
942
+ return (_jsxs(Box, { flexDirection: 'column', children: [_jsx(DryRunBanner, { action: 'install', count: state.plan.totalCount, tools: state.tools, where: label }), _jsx(Text, { children: " " }), _jsx(Text, { children: _jsxs(Text, { bold: true, color: 'magenta', children: ["Org: @", orgName] }) }), _jsx(Text, { children: " " }), _jsx(PlanEntryLines, { plan: state.plan }), _jsx(CrossScopeWarning, { installs: state.crossScope ?? [] })] }));
771
943
  }
772
944
  if (state.phase === 'done') {
773
945
  if (effectiveJson) {
@@ -783,11 +955,6 @@ function InstallOrg({ cliOrg, dryRun, force, json, orgName, pin, projectRoot, sk
783
955
  }
784
956
  return null;
785
957
  }
786
- /** Whether a lockfile asset was installed as part of this bundle (name, org) at this version. */
787
- function isFromBundleVersion(installed, bundle, version) {
788
- const origin = installed.bundleOrigin;
789
- return origin?.name === bundle.name && origin.org === bundle.org && origin.version === version;
790
- }
791
958
  /** A member as a report names it: its spec and type. */
792
959
  function memberLabel({ name, org, type }) {
793
960
  return `${formatOrgName(org, name)} (${type})`;
@@ -807,4 +974,55 @@ function membersOutsideBundle(lockfile, members, bundle) {
807
974
  return [{ ...(origin ? { bundle: origin } : {}), ...member }];
808
975
  });
809
976
  }
977
+ /** The names a run of several left out of its plan: already installed, or made a direct install. */
978
+ function NamedAssetLines({ assets, dryRun = false, where, }) {
979
+ const inScope = where ? ` in ${where}` : '';
980
+ return (_jsx(_Fragment, { children: assets
981
+ .filter((a) => a.outcome !== 'installed')
982
+ .map((a) => (_jsxs(Text, { children: [a.outcome === 'upgraded-to-direct' ? _jsx(Text, { color: 'green', children: "\u2713" }) : _jsx(Text, { color: 'yellow', children: "!" }), ' ', _jsx(Text, { bold: true, children: formatOrgName(a.org, a.name) }), " (v", a.version, ")", ' ', a.outcome === 'upgraded-to-direct'
983
+ ? `${dryRun ? 'would become' : 'is now'} a direct install${inScope}.`
984
+ : `is already installed${inScope}.`] }, identityKey(a)))) }));
985
+ }
986
+ /**
987
+ * The error for a bundle name that only another scope has, when no asset can claim the name
988
+ * either, instead of a misleading "asset not found".
989
+ */
990
+ function otherScopeBundleError(registry, name, inlineOrg) {
991
+ if (inlineOrg) {
992
+ return new Error(`Bundle '${name}' not found in org '${inlineOrg}'.`);
993
+ }
994
+ const orgScopes = (registry.bundles ?? []).filter((b) => b.name === name && b.org).map((b) => `@${b.org}/${name}`);
995
+ return new Error(`Bundle '${name}' not found in the registry.` +
996
+ (orgScopes.length > 0 ? ` Did you mean ${orgScopes.join(' or ')}?` : ''));
997
+ }
998
+ /** A plan's entries, each with its type and version, its dependencies marked. */
999
+ function PlanEntryLines({ plan }) {
1000
+ return (_jsx(_Fragment, { children: plan.entries.map((entry) => (_jsxs(Text, { children: [' ', entry.reason === 'dependency' ? (_jsxs(Text, { color: 'cyan', children: [formatOrgName(entry.asset.org, entry.asset.name), ' ', _jsxs(Text, { dimColor: true, children: ["(", entry.asset.type, ") v", entry.version, " \u2014 dependency"] })] })) : (_jsxs(Text, { children: [_jsx(Text, { bold: true, children: formatOrgName(entry.asset.org, entry.asset.name) }), _jsxs(Text, { dimColor: true, children: [' ', "(", entry.asset.type, ") v", entry.version] })] }))] }, identityKey(entry.asset)))) }));
1001
+ }
1002
+ /**
1003
+ * The members not installed yet that the plan installs without the bundle's origin: named on their
1004
+ * own, or with another bundle named before it. A dry run reports them as an install would.
1005
+ */
1006
+ function plannedOutsideBundle(plan, lockfile, members, bundle) {
1007
+ return members.flatMap((member) => {
1008
+ if (findInstalledAsset(lockfile, member.name, member.type, member.org))
1009
+ return [];
1010
+ const entry = plan.entries.find((e) => identityKey(e.asset) === identityKey(member));
1011
+ const origin = entry?.bundleOrigin;
1012
+ if (!entry || (origin?.name === bundle.name && origin.org === bundle.org))
1013
+ return [];
1014
+ return [{ ...(origin ? { bundle: origin } : {}), ...member }];
1015
+ });
1016
+ }
1017
+ /**
1018
+ * The bundles and orgs a run of several names had among them: what it did with each bundle, the
1019
+ * bundle's members outside it, and how many assets each org brought.
1020
+ */
1021
+ function TargetLines({ bundles = [], dryRun = false, orgs = [], where, }) {
1022
+ const inScope = where ? ` in ${where}` : '';
1023
+ return (_jsxs(_Fragment, { children: [bundles.map((bundle) => {
1024
+ const spec = formatOrgName(bundle.org, bundle.name);
1025
+ return (_jsxs(Fragment, { children: [bundle.outcome === 'already-installed' ? (_jsxs(Text, { children: [_jsx(Text, { color: 'yellow', children: "!" }), " Bundle ", _jsx(Text, { bold: true, children: spec }), " is already fully installed (v", bundle.version, ")", inScope, "."] })) : (_jsxs(Text, { children: [_jsx(Text, { color: 'green', children: "\u2713" }), " Bundle ", _jsx(Text, { bold: true, children: spec }), " v", bundle.version, ' ', dryRun ? 'would be installed' : 'installed', inScope, "."] })), _jsx(BundleReportLines, { bundleName: spec, dryRun: dryRun, report: bundle })] }, spec));
1026
+ }), orgs.map(({ count, org }) => (_jsxs(Text, { children: [_jsxs(Text, { bold: true, color: 'magenta', children: ["Org: @", org] }), _jsxs(Text, { dimColor: true, children: [' ', "\u2014 all ", count, " asset", count !== 1 ? 's' : ''] })] }, org)))] }));
1027
+ }
810
1028
  //# sourceMappingURL=install.js.map