rman 1.2.5 → 2.0.0-beta.2

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 (153) hide show
  1. package/README.md +63 -19
  2. package/cli.d.ts +5 -0
  3. package/cli.js +214 -88
  4. package/commands/build.command.d.ts +177 -3
  5. package/commands/build.command.js +20 -10
  6. package/commands/changed.command.d.ts +80 -3
  7. package/commands/changed.command.js +19 -12
  8. package/commands/changelog.command.d.ts +192 -3
  9. package/commands/changelog.command.js +87 -43
  10. package/commands/config.command.d.ts +44 -3
  11. package/commands/config.command.js +30 -19
  12. package/commands/diff.command.d.ts +37 -3
  13. package/commands/diff.command.js +25 -16
  14. package/commands/exec.command.d.ts +193 -3
  15. package/commands/exec.command.js +60 -58
  16. package/commands/github-release.command.d.ts +154 -3
  17. package/commands/github-release.command.js +67 -37
  18. package/commands/import.command.d.ts +36 -3
  19. package/commands/import.command.js +28 -20
  20. package/commands/info.command.d.ts +35 -7
  21. package/commands/info.command.js +36 -30
  22. package/commands/list.command.d.ts +163 -3
  23. package/commands/list.command.js +109 -71
  24. package/commands/publish.command.d.ts +231 -0
  25. package/commands/publish.command.js +304 -0
  26. package/commands/run.command.d.ts +186 -6
  27. package/commands/run.command.js +26 -72
  28. package/commands/test.command.d.ts +173 -3
  29. package/commands/test.command.js +16 -10
  30. package/commands/version.command.d.ts +317 -3
  31. package/commands/version.command.js +149 -69
  32. package/commands.d.ts +32 -0
  33. package/commands.js +28 -0
  34. package/constants.js +1 -1
  35. package/core/application.d.ts +116 -0
  36. package/core/application.js +143 -0
  37. package/core/command-builder.d.ts +14 -0
  38. package/core/command-builder.js +78 -0
  39. package/core/config.d.ts +180 -50
  40. package/core/config.js +332 -153
  41. package/core/core-services.d.ts +14 -0
  42. package/core/core-services.js +30 -0
  43. package/core/core-targets.d.ts +14 -0
  44. package/core/core-targets.js +16 -0
  45. package/core/custom-command.d.ts +42 -6
  46. package/core/custom-command.js +44 -17
  47. package/core/extends-config.d.ts +13 -5
  48. package/core/extends-config.js +52 -12
  49. package/core/load-config-module.d.ts +28 -0
  50. package/core/load-config-module.js +42 -0
  51. package/core/manifest.d.ts +47 -25
  52. package/core/manifest.js +51 -69
  53. package/core/merge-config.d.ts +33 -34
  54. package/core/merge-config.js +138 -93
  55. package/core/package.d.ts +145 -17
  56. package/core/package.js +128 -36
  57. package/core/plugin-loader.d.ts +65 -0
  58. package/core/plugin-loader.js +234 -0
  59. package/core/plugin.d.ts +135 -90
  60. package/core/plugin.js +70 -173
  61. package/core/publish-target.d.ts +124 -0
  62. package/core/publish-target.js +30 -0
  63. package/core/registry.d.ts +30 -0
  64. package/core/registry.js +47 -0
  65. package/core/repository.d.ts +72 -9
  66. package/core/repository.js +293 -43
  67. package/core/resolve-target.d.ts +1 -1
  68. package/core/resolve-target.js +1 -1
  69. package/core/service.d.ts +49 -0
  70. package/core/service.js +40 -0
  71. package/core/version-scheme.d.ts +23 -1
  72. package/core/version-scheme.js +29 -1
  73. package/core/workspace.d.ts +84 -33
  74. package/core/workspace.js +63 -22
  75. package/index.d.ts +111 -14
  76. package/index.js +85 -9
  77. package/interfaces/rman-config.interface.d.ts +739 -212
  78. package/interfaces/rman-config.interface.js +61 -1
  79. package/package.json +2 -1
  80. package/plugins/builtins.d.ts +44 -0
  81. package/plugins/builtins.js +33 -0
  82. package/plugins/detect.d.ts +78 -0
  83. package/plugins/detect.js +70 -0
  84. package/plugins/node/augmentation/rman.augmentation.d.ts +84 -0
  85. package/plugins/node/augmentation/rman.augmentation.js +1 -0
  86. package/plugins/node/augmentation/system-info.augmentation.d.ts +26 -0
  87. package/plugins/node/augmentation/system-info.augmentation.js +79 -0
  88. package/plugins/node/commands/ci.command.d.ts +131 -0
  89. package/plugins/node/commands/ci.command.js +59 -0
  90. package/plugins/node/commands/clean.command.d.ts +183 -0
  91. package/plugins/node/commands/clean.command.js +73 -0
  92. package/plugins/node/index.d.ts +29 -0
  93. package/plugins/node/index.js +40 -0
  94. package/plugins/node/node-config.interface.d.ts +77 -0
  95. package/plugins/node/node-config.interface.js +7 -0
  96. package/plugins/node/node-manifest.provider.d.ts +68 -0
  97. package/plugins/node/node-manifest.provider.js +125 -0
  98. package/plugins/node/node.platform.d.ts +53 -0
  99. package/plugins/node/node.platform.js +134 -0
  100. package/plugins/node/npm-publish-target.d.ts +73 -0
  101. package/plugins/node/npm-publish-target.js +96 -0
  102. package/plugins/node/services/ci.service.d.ts +47 -0
  103. package/plugins/node/services/ci.service.js +213 -0
  104. package/plugins/node/services/clean.service.d.ts +53 -0
  105. package/plugins/node/services/clean.service.js +237 -0
  106. package/plugins/node/services/publish.service.d.ts +114 -0
  107. package/plugins/node/services/publish.service.js +371 -0
  108. package/plugins/node/services/version-plan.service.d.ts +44 -0
  109. package/plugins/node/services/version-plan.service.js +58 -0
  110. package/plugins/node/utils/npm-view.d.ts +48 -0
  111. package/plugins/node/utils/npm-view.js +71 -0
  112. package/plugins/node/utils/workspace-range.d.ts +26 -0
  113. package/plugins/node/utils/workspace-range.js +28 -0
  114. package/services/change-hash.service.d.ts +2 -2
  115. package/services/change-hash.service.js +2 -2
  116. package/services/changelog.service.d.ts +62 -51
  117. package/services/changelog.service.js +14 -11
  118. package/services/docker-publish.service.d.ts +50 -29
  119. package/services/docker-publish.service.js +43 -20
  120. package/services/exec.service.d.ts +23 -12
  121. package/services/exec.service.js +14 -9
  122. package/services/github-release.service.d.ts +44 -33
  123. package/services/github-release.service.js +13 -10
  124. package/services/import.service.d.ts +25 -14
  125. package/services/import.service.js +9 -5
  126. package/services/list.service.d.ts +62 -10
  127. package/services/list.service.js +62 -15
  128. package/services/run.service.d.ts +22 -13
  129. package/services/run.service.js +262 -223
  130. package/services/version-plan.service.d.ts +27 -4
  131. package/services/version-plan.service.js +42 -15
  132. package/services/version.service.d.ts +31 -11
  133. package/services/version.service.js +29 -13
  134. package/targets/docker.target.d.ts +53 -0
  135. package/targets/docker.target.js +40 -0
  136. package/utils/bin-path.d.ts +6 -7
  137. package/utils/bin-path.js +7 -18
  138. package/utils/branch-guard.d.ts +29 -0
  139. package/utils/branch-guard.js +31 -0
  140. package/utils/exec.d.ts +10 -0
  141. package/utils/exec.js +1 -1
  142. package/utils/logger.d.ts +1 -1
  143. package/utils/logger.js +1 -1
  144. package/utils/package-filter.d.ts +127 -7
  145. package/utils/package-filter.js +197 -16
  146. package/utils/printable-config.d.ts +1 -1
  147. package/utils/printable-config.js +1 -1
  148. package/utils/run-bin.d.ts +10 -0
  149. package/utils/run-bin.js +1 -1
  150. package/utils/run-options.d.ts +97 -0
  151. package/utils/run-options.js +81 -0
  152. package/utils/version-stamp.d.ts +1 -1
  153. package/utils/version-stamp.js +1 -1
@@ -1,11 +1,110 @@
1
1
  import readline from 'node:readline/promises';
2
2
  import colors from 'ansi-colors';
3
3
  import EasyTable from 'easy-table';
4
- import { VersionService } from '../services/version.service.js';
4
+ import { registerCommand } from '../interfaces/rman-config.interface.js';
5
5
  import { VersionPlanService } from '../services/version-plan.service.js';
6
- import { applyBranchGuardOptions, assertAllowedBranch, readBranchGuardOptions } from '../utils/branch-guard.js';
7
- import { applyPackageFilterOptions, readPackageFilterOptions } from '../utils/package-filter.js';
8
- export function initCli(repository, program) {
6
+ import { assertAllowedBranch, branchGuardOptions, readBranchGuardOptions } from '../utils/branch-guard.js';
7
+ import { packageFilterOptions, readPackageFilterOptions } from '../utils/package-filter.js';
8
+ /**
9
+ * Hoisted out of the metadata literal so the handler can be annotated against them - see
10
+ * `RmanConfig.ArgsOf` for why an inferred `argv` and the metadata's own typo checking cannot both
11
+ * work in one signature.
12
+ *
13
+ * `as const` on the command string is load-bearing twice over: the config key is derived from it
14
+ * (`'version'`), and so are the positional names it declares.
15
+ */
16
+ const COMMAND = 'version [bump]';
17
+ const config = {
18
+ /** The two shared groups, spread rather than applied - this is what
19
+ * `applyBranchGuardOptions(applyPackageFilterOptions(cmd))` used to say. Spread first, so an
20
+ * option deliberately overridden below wins. */
21
+ ...packageFilterOptions,
22
+ ...branchGuardOptions,
23
+ interactive: {
24
+ target: 'cli',
25
+ alias: 'i',
26
+ describe: 'Show the plan and ask for confirmation before applying (with or without an explicit bump)',
27
+ type: 'boolean',
28
+ },
29
+ show: {
30
+ target: 'cli',
31
+ describe: 'Show the resulting plan for the given bump without applying it - unlike omitting bump ' +
32
+ 'entirely, this still uses the given bump keyword/version to compute the plan, ' +
33
+ 'just never writes it.',
34
+ type: 'boolean',
35
+ conflicts: 'interactive',
36
+ },
37
+ yes: {
38
+ target: 'cli',
39
+ alias: 'y',
40
+ describe: 'Skip the confirmation prompt and apply the computed plan immediately - an auto-detected ' +
41
+ 'bump included, no explicit bump keyword required (same idea as "publish --yes").',
42
+ type: 'boolean',
43
+ conflicts: 'interactive',
44
+ },
45
+ ignoreDirty: {
46
+ target: 'cli',
47
+ cliName: 'ignore-dirty',
48
+ describe: 'Exclude a package with uncommitted local changes instead of aborting the whole run',
49
+ type: 'boolean',
50
+ },
51
+ push: {
52
+ target: 'cli',
53
+ describe: 'Push the resulting commit(s) and tag(s) to the remote once applied',
54
+ type: 'boolean',
55
+ },
56
+ message: {
57
+ target: 'cli',
58
+ alias: 'm',
59
+ describe: 'Override the commit message for every group this run commits (default: .rmanrc ' +
60
+ 'version.commitMessage, or "chore(release): v{version}") - "{version}" is substituted ' +
61
+ "when a commit's own group shares one version.",
62
+ type: 'string',
63
+ },
64
+ changelog: {
65
+ target: 'both',
66
+ describe: "Also write each bumped package's CHANGELOG.md (same as running changelog --write " +
67
+ 'separately) and fold it into the same commit as its version bump. Default: .rmanrc ' +
68
+ '"version.changelog", or false - --no-changelog forces it off even when that\'s true.',
69
+ type: 'boolean',
70
+ },
71
+ preid: {
72
+ target: 'cli',
73
+ describe: 'Make the bump a prerelease with this identifier (e.g. "beta" -> 1.2.3-beta.0). ' +
74
+ 'Running again with the same --preid increments it (-> 1.2.3-beta.1); a different ' +
75
+ 'identifier starts a fresh prerelease line. Ignored when bump is an explicit version.',
76
+ type: 'string',
77
+ },
78
+ /**
79
+ * Config-only, from here down: `.rmanrc "version.*"` keys with no reason to be a flag.
80
+ * `--message` is `commitMessage`'s flag and is declared above; the rest are settings a repository
81
+ * states once, not things a single run overrides.
82
+ */
83
+ commitMessage: {
84
+ target: 'config',
85
+ describe: 'The commit message for each group this run commits - "{version}" is substituted when a ' +
86
+ 'commit\'s own group shares one version. Default "chore(release): v{version}".',
87
+ type: 'string',
88
+ },
89
+ releaseTagPattern: {
90
+ target: 'config',
91
+ describe: "Tag naming the repository's own release, as opposed to the per-package tags " +
92
+ '"changelog.tagPattern" names - only created when the root is on a calendar version. ' +
93
+ 'Root-level only. Default "release-*". Must **not** match any package\'s own tag pattern, or ' +
94
+ "that package's changelog boundary resolves to the repository release instead of its own.",
95
+ type: 'string',
96
+ },
97
+ stampDockerfile: {
98
+ target: 'config',
99
+ describe: "Keep this package's Dockerfile org.opencontainers.image.version label in step with the " +
100
+ 'version being written. Per-package cascaded. Default true - the label is by specification ' +
101
+ 'the version of the packaged software, so there is only one correct value and "version" is ' +
102
+ 'what knows it. Only ever rewrites a label the Dockerfile already declares.',
103
+ type: 'boolean',
104
+ },
105
+ };
106
+ const versionCommand = registerCommand(app => {
107
+ const repository = app.repository;
9
108
  /** `rman version <bump|version>` is validated by the root's scheme (see `getPlan`), so the help
10
109
  * has to name that scheme's own words rather than semver's - otherwise `--help` in a repository
11
110
  * numbering some other way documents keywords its own planner would reject. A four-part scheme
@@ -15,72 +114,52 @@ export function initCli(repository, program) {
15
114
  /** For the examples. Not `smallestBump`, which throws: `--help` must still render for a scheme
16
115
  * that declares none, and the placeholder says what to write there. */
17
116
  const smallest = bumpNames[0] ?? '<bump>';
18
- program.command({
19
- command: 'version [bump]',
20
- /** `changelog` too: `--changelog` folds one into the bump commit, and `group` is what decides
21
- * which packages move together. */
22
- configKeys: ['version', 'changelog', 'group'],
117
+ return {
118
+ /** `as const` so the config key stays derivable from it: `CommandMetadata['command']` is a
119
+ * plain `string`, so without the assertion inference widens `'version [bump]'` and there is
120
+ * nothing left to read `'version'` out of. */
121
+ command: COMMAND,
23
122
  describe: 'Bumps versions of changed packages (and their dependents), grouped via .rmanrc "group"',
24
- builder: cmd => applyBranchGuardOptions(applyPackageFilterOptions(cmd))
25
- .example(`$0 version ${smallest}`, `# Bump ${smallest} directly, applied immediately`)
26
- .example('$0 version', "# Auto-detect the bump from commits, show the plan, don't write anything")
27
- .example('$0 version --interactive', '# Show the plan either way, then ask for confirmation')
28
- .example(`$0 version ${smallest} --show`, `# Preview what an explicit ${smallest} would do, without applying it`)
29
- .positional('bump', {
30
- describe: `A bump keyword (${bumps}) or an explicit ${scheme} version. ` +
31
- 'Omit to auto-detect from commits and only preview the plan.',
32
- type: 'string',
33
- })
34
- .option('interactive', {
35
- alias: 'i',
36
- describe: 'Show the plan and ask for confirmation before applying (with or without an explicit bump)',
37
- type: 'boolean',
38
- })
39
- .option('show', {
40
- describe: 'Show the resulting plan for the given bump without applying it - unlike omitting bump ' +
41
- 'entirely, this still uses the given bump keyword/version to compute the plan, ' +
42
- 'just never writes it.',
43
- type: 'boolean',
44
- })
45
- .conflicts('show', 'interactive')
46
- .option('yes', {
47
- alias: 'y',
48
- describe: 'Skip the confirmation prompt and apply the computed plan immediately - an auto-detected ' +
49
- 'bump included, no explicit bump keyword required (same idea as "publish --yes").',
50
- type: 'boolean',
51
- })
52
- .conflicts('yes', 'interactive')
53
- .option('ignore-dirty', {
54
- describe: 'Exclude a package with uncommitted local changes instead of aborting the whole run',
55
- type: 'boolean',
56
- })
57
- .option('push', {
58
- describe: 'Push the resulting commit(s) and tag(s) to the remote once applied',
59
- type: 'boolean',
60
- })
61
- .option('message', {
62
- alias: 'm',
63
- describe: 'Override the commit message for every group this run commits (default: .rmanrc ' +
64
- 'version.commitMessage, or "chore(release): v{version}") - "{version}" is substituted ' +
65
- "when a commit's own group shares one version.",
66
- type: 'string',
67
- })
68
- .option('changelog', {
69
- describe: "Also write each bumped package's CHANGELOG.md (same as running changelog --write " +
70
- 'separately) and fold it into the same commit as its version bump. Default: .rmanrc ' +
71
- '"version.changelog", or false - --no-changelog forces it off even when that\'s true.',
72
- type: 'boolean',
73
- })
74
- .option('preid', {
75
- describe: 'Make the bump a prerelease with this identifier (e.g. "beta" -> 1.2.3-beta.0). ' +
76
- 'Running again with the same --preid increments it (-> 1.2.3-beta.1); a different ' +
77
- `identifier starts a fresh prerelease line. Ignored when bump is an explicit ${scheme} version.`,
78
- type: 'string',
79
- }),
123
+ /**
124
+ * Read, not owned - so they are named here rather than declared in `config`. `group` is a core
125
+ * per-package key that decides which packages move together, `changelog.*` is consulted when
126
+ * `--changelog` folds one into the bump commit, and `allowBranch`/`ignoreBranch` are repo-wide
127
+ * keys the shared branch-guard group only exposes as flags. `version` itself is not listed: it
128
+ * is this command's own key, derived from `command`.
129
+ */
130
+ configKeys: ['changelog', 'group', 'allowBranch', 'ignoreBranch'],
131
+ config,
132
+ examples: [
133
+ {
134
+ command: `$0 version ${smallest} --show`,
135
+ description: `# Bump ${smallest} directly, applied immediately`,
136
+ },
137
+ {
138
+ command: '$0 version',
139
+ description: "# Auto-detect the bump from commits, show the plan, don't write anything",
140
+ },
141
+ {
142
+ command: '$0 version --interactive',
143
+ description: '# Show the plan either way, then ask for confirmation',
144
+ },
145
+ {
146
+ command: `$0 version ${smallest} --show`,
147
+ description: `# Preview what an explicit ${smallest} would do, without applying it`,
148
+ },
149
+ ],
150
+ positionals: {
151
+ bump: {
152
+ describe: `A bump keyword (${bumps}) or an explicit ${scheme} version. ` +
153
+ 'Omit to auto-detect from commits and only preview the plan.',
154
+ type: 'string',
155
+ },
156
+ },
157
+ /** Annotated rather than inferred - `RmanConfig.ArgsOf` records why, and the casts this
158
+ * replaces are the reason it is worth two hoisted declarations. */
80
159
  handler: async (args) => {
81
160
  await assertAllowedBranch(repository, readBranchGuardOptions(args));
82
161
  const bump = args.bump;
83
- const plan = await VersionPlanService.getPlanner().getPlan(repository, {
162
+ const plan = await VersionPlanService.getPlanner(app).getPlan(repository, {
84
163
  ...readPackageFilterOptions(args),
85
164
  bump,
86
165
  ignoreDirty: args.ignoreDirty,
@@ -115,15 +194,16 @@ export function initCli(repository, program) {
115
194
  if (!apply)
116
195
  return;
117
196
  const changelog = args.changelog ?? repository.config?.version?.changelog ?? false;
118
- const applied = await VersionService.applyPlan(repository, plan, {
197
+ const applied = await app.getService('version').applyPlan(plan, {
119
198
  push: args.push,
120
199
  message: args.message,
121
200
  changelog,
122
201
  });
123
202
  printApplied(applied);
124
203
  },
125
- });
126
- }
204
+ };
205
+ });
206
+ export default versionCommand;
127
207
  /**
128
208
  * What the run *did* - which is deliberately not the table again.
129
209
  *
package/commands.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * **Every built-in command, and the one place the list lives.**
3
+ *
4
+ * Imported for their side effect: each module calls `registerCommand`, which pushes its register
5
+ * function onto `commandRegistry`. Nothing reads the modules' exports - `runCli` walks the registry
6
+ * - so this import list *is* the built-in command list.
7
+ *
8
+ * **`index.ts` imports this too, and that is not tidiness.** Each module also carries a
9
+ * `declare module` augmentation contributing its own `.rmanrc` keys, and a type augmentation only
10
+ * applies where the module declaring it is part of the program. Reached only from `cli.ts`, those
11
+ * keys existed for rman itself and for nobody else: `rman-node` reading `pkg.config.publish` got
12
+ * `Property 'publish' does not exist on type 'RmanConfig'` (measured, the moment the keys stopped
13
+ * being hand-written centrally, in what is now `rman-config.interface.ts`).
14
+ */
15
+ import './commands/build.command.js';
16
+ import './commands/changed.command.js';
17
+ import './commands/changelog.command.js';
18
+ import './commands/config.command.js';
19
+ import './commands/diff.command.js';
20
+ import './commands/exec.command.js';
21
+ import './commands/github-release.command.js';
22
+ import './commands/import.command.js';
23
+ import './commands/info.command.js';
24
+ import './commands/list.command.js';
25
+ import './commands/publish.command.js';
26
+ import './commands/run.command.js';
27
+ import './commands/test.command.js';
28
+ import './commands/version.command.js';
29
+ /** The config shapes a command declares by hand, where an option cannot describe them - see
30
+ * `RmanConfig.CommandContribution`'s `Extra`. Exported so a plugin can name one. */
31
+ export type { PublishExtraKeys, PublishTargetConfigs } from './commands/publish.command.js';
32
+ export type { VersionExtraKeys, VersionStampEntry } from './commands/version.command.js';
package/commands.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * **Every built-in command, and the one place the list lives.**
3
+ *
4
+ * Imported for their side effect: each module calls `registerCommand`, which pushes its register
5
+ * function onto `commandRegistry`. Nothing reads the modules' exports - `runCli` walks the registry
6
+ * - so this import list *is* the built-in command list.
7
+ *
8
+ * **`index.ts` imports this too, and that is not tidiness.** Each module also carries a
9
+ * `declare module` augmentation contributing its own `.rmanrc` keys, and a type augmentation only
10
+ * applies where the module declaring it is part of the program. Reached only from `cli.ts`, those
11
+ * keys existed for rman itself and for nobody else: `rman-node` reading `pkg.config.publish` got
12
+ * `Property 'publish' does not exist on type 'RmanConfig'` (measured, the moment the keys stopped
13
+ * being hand-written centrally, in what is now `rman-config.interface.ts`).
14
+ */
15
+ import './commands/build.command.js';
16
+ import './commands/changed.command.js';
17
+ import './commands/changelog.command.js';
18
+ import './commands/config.command.js';
19
+ import './commands/diff.command.js';
20
+ import './commands/exec.command.js';
21
+ import './commands/github-release.command.js';
22
+ import './commands/import.command.js';
23
+ import './commands/info.command.js';
24
+ import './commands/list.command.js';
25
+ import './commands/publish.command.js';
26
+ import './commands/run.command.js';
27
+ import './commands/test.command.js';
28
+ import './commands/version.command.js';
package/constants.js CHANGED
@@ -1 +1 @@
1
- export const version = '1.2.5';
1
+ export const version = '2.0.0-beta.2';
@@ -0,0 +1,116 @@
1
+ import type { VersionPlanService } from '../services/version-plan.service.js';
2
+ import { Logger, type LogLevel } from '../utils/logger.js';
3
+ import { type Platform, type Plugin } from './plugin.js';
4
+ import type { PublishTarget } from './publish-target.js';
5
+ import { Registry } from './registry.js';
6
+ import type { Repository } from './repository.js';
7
+ import type { ServiceFactory, ServiceMap } from './service.js';
8
+ /**
9
+ * **One rman invocation, and everything it holds.** Created before anything else, handed to every
10
+ * plugin's `init`, and the owner of every registry and service that used to be a module-level
11
+ * variable.
12
+ *
13
+ * The point is that nothing is process-global any more. `Manifest`, `Workspace`, `BinPath`,
14
+ * `RunService` and `VersionPlanService` each kept their contributions in a module-scope array, so
15
+ * two repositories in one process shared them - which the test suite could only survive with a
16
+ * root hook emptying five of them before every test, and the failure that hook prevented was:
17
+ * whichever spec ran first decided the answer for the rest, and the core appeared to work in tests
18
+ * that had registered nothing. An application starts empty and is thrown away whole, so the hook
19
+ * is gone.
20
+ */
21
+ export declare class RmanApplication {
22
+ /**
23
+ * The **technologies** this invocation knows about, in `plugins` declaration order.
24
+ *
25
+ * One registry rather than the four it replaces (`manifest`, `workspace`, `binPaths`,
26
+ * `runSteps`): a technology is a whole, and declaring part of one was never meaningful - see
27
+ * `Platform`.
28
+ *
29
+ * **Flattened out of the plugins**, because this is what every seam actually iterates - a
30
+ * manifest is read by a platform, a workspace is laid out by a platform, a PATH is contributed by
31
+ * a platform. A plugin providing two of them puts two entries here, and a plugin providing none
32
+ * puts none.
33
+ */
34
+ readonly platforms: Registry<Platform>;
35
+ /**
36
+ * The **plugins** themselves, in declaration order - what a config named, before the platforms
37
+ * were taken out of them.
38
+ *
39
+ * Kept apart from `platforms` because the two answer different questions: this is who contributed,
40
+ * that is what they contributed. `init` runs per plugin; nothing else reads this.
41
+ */
42
+ readonly plugins: Registry<Plugin>;
43
+ /**
44
+ * Where a package's artifact can ship, in registration order.
45
+ *
46
+ * A registry rather than a field because the answer is a *sum*: a package may ship to npm and
47
+ * Docker Hub at once, and `publish` runs every target a package declares. The core contributes
48
+ * `docker` (nobody's ecosystem), the `node` built-in contributes `npm`, and a plugin for any other
49
+ * technology adds its own without either of them changing - see `PublishTarget`.
50
+ */
51
+ readonly publishTargets: Registry<PublishTarget>;
52
+ /**
53
+ * **The planner that orchestrates a run**, not the one that answers for each package.
54
+ *
55
+ * One answer, not a sum - so a field rather than a registry, and last registration wins. What it
56
+ * decides is the shape of the whole plan: groups, the commit→size reading, the cross-group ripple
57
+ * and the root's release identity, none of which belongs to any one technology.
58
+ *
59
+ * The two decisions that *are* a technology's - `detectBoundary` and `cascade` - are asked of
60
+ * each package's own `Platform.versionPlanner` instead (`VersionPlanService.plannerFor`), so a
61
+ * polyglot repository no longer resolves both through whichever plugin registered last.
62
+ */
63
+ versionPlanner?: VersionPlanService;
64
+ readonly logger: Logger;
65
+ /** Which platform claims a directory - the first whose manifest provider recognizes it, because
66
+ * before a package is read there is nothing else to go on. `basePlatform` when none does, so the
67
+ * caller needs no guard. */
68
+ platformFor(dir: string): Platform;
69
+ /**
70
+ * **Not a constructor field, and that is forced by the order things happen in.** Plugins are what
71
+ * *find* the packages - `Repository.create` loads them before it calls `Workspace.resolve` - so
72
+ * the application has to exist, and be handed to every `init`, while there is still no
73
+ * repository to put in it.
74
+ *
75
+ * A throwing getter rather than `undefined`: a plugin reaching for packages during `init` has
76
+ * made a real mistake, and `undefined` would let it write a check that silently does nothing.
77
+ */
78
+ get repository(): Repository;
79
+ /** `info` until `--log-level` or `.rmanrc "logLevel"` is resolved - which cannot happen here,
80
+ * since reading the config is itself work the application does. */
81
+ constructor(options?: {
82
+ logLevel?: LogLevel;
83
+ });
84
+ /**
85
+ * The single instance of a service, built on first use.
86
+ *
87
+ * **Lazy, and for two reasons.** `rman info` has no business constructing the changelog, version
88
+ * and release services, which is the same laziness the config scope's `git` getter was measured
89
+ * to need (0 git reads with property descriptors, 1 with a spread). And services call each other -
90
+ * `VersionService` reaches for `RunService` and `ChangelogService`, which reaches for
91
+ * `ChangeHashService` - so resolving at call time is what keeps that from being a construction
92
+ * cycle.
93
+ */
94
+ getService<K extends keyof ServiceMap>(name: K): ServiceMap[K];
95
+ /**
96
+ * Registers how a service is built. A plugin replacing one of the core's - a version planner that
97
+ * knows its own ecosystem's dependency ranges - does it here, which is why that no longer needs a
98
+ * seam of its own on the plugin interface.
99
+ *
100
+ * Refused once the service has been built: something is already holding the old instance, and
101
+ * swapping the factory then would leave two answers to one question in play.
102
+ */
103
+ setService<K extends keyof ServiceMap>(name: K, factory: ServiceFactory<ServiceMap[K]>): void;
104
+ /**
105
+ * Called by `Repository.create`, after the plugins that find the packages have run.
106
+ *
107
+ * **One application, one repository**, and a second is refused. It was briefly "last one wins",
108
+ * because a single shared application made two repositories in one test collide; each
109
+ * `Repository.create` makes its own now, so the rule holds again and nothing in a process is
110
+ * shared by accident.
111
+ */
112
+ attachRepository(repository: Repository): void;
113
+ private _repository?;
114
+ private readonly services;
115
+ private readonly factories;
116
+ }
@@ -0,0 +1,143 @@
1
+ import { Logger } from '../utils/logger.js';
2
+ import { registerCoreServices } from './core-services.js';
3
+ import { registerCoreTargets } from './core-targets.js';
4
+ import { basePlatform } from './plugin.js';
5
+ import { Registry } from './registry.js';
6
+ /**
7
+ * **One rman invocation, and everything it holds.** Created before anything else, handed to every
8
+ * plugin's `init`, and the owner of every registry and service that used to be a module-level
9
+ * variable.
10
+ *
11
+ * The point is that nothing is process-global any more. `Manifest`, `Workspace`, `BinPath`,
12
+ * `RunService` and `VersionPlanService` each kept their contributions in a module-scope array, so
13
+ * two repositories in one process shared them - which the test suite could only survive with a
14
+ * root hook emptying five of them before every test, and the failure that hook prevented was:
15
+ * whichever spec ran first decided the answer for the rest, and the core appeared to work in tests
16
+ * that had registered nothing. An application starts empty and is thrown away whole, so the hook
17
+ * is gone.
18
+ */
19
+ export class RmanApplication {
20
+ /**
21
+ * The **technologies** this invocation knows about, in `plugins` declaration order.
22
+ *
23
+ * One registry rather than the four it replaces (`manifest`, `workspace`, `binPaths`,
24
+ * `runSteps`): a technology is a whole, and declaring part of one was never meaningful - see
25
+ * `Platform`.
26
+ *
27
+ * **Flattened out of the plugins**, because this is what every seam actually iterates - a
28
+ * manifest is read by a platform, a workspace is laid out by a platform, a PATH is contributed by
29
+ * a platform. A plugin providing two of them puts two entries here, and a plugin providing none
30
+ * puts none.
31
+ */
32
+ platforms = new Registry();
33
+ /**
34
+ * The **plugins** themselves, in declaration order - what a config named, before the platforms
35
+ * were taken out of them.
36
+ *
37
+ * Kept apart from `platforms` because the two answer different questions: this is who contributed,
38
+ * that is what they contributed. `init` runs per plugin; nothing else reads this.
39
+ */
40
+ plugins = new Registry();
41
+ /**
42
+ * Where a package's artifact can ship, in registration order.
43
+ *
44
+ * A registry rather than a field because the answer is a *sum*: a package may ship to npm and
45
+ * Docker Hub at once, and `publish` runs every target a package declares. The core contributes
46
+ * `docker` (nobody's ecosystem), the `node` built-in contributes `npm`, and a plugin for any other
47
+ * technology adds its own without either of them changing - see `PublishTarget`.
48
+ */
49
+ publishTargets = new Registry();
50
+ /**
51
+ * **The planner that orchestrates a run**, not the one that answers for each package.
52
+ *
53
+ * One answer, not a sum - so a field rather than a registry, and last registration wins. What it
54
+ * decides is the shape of the whole plan: groups, the commit→size reading, the cross-group ripple
55
+ * and the root's release identity, none of which belongs to any one technology.
56
+ *
57
+ * The two decisions that *are* a technology's - `detectBoundary` and `cascade` - are asked of
58
+ * each package's own `Platform.versionPlanner` instead (`VersionPlanService.plannerFor`), so a
59
+ * polyglot repository no longer resolves both through whichever plugin registered last.
60
+ */
61
+ versionPlanner;
62
+ logger;
63
+ /** Which platform claims a directory - the first whose manifest provider recognizes it, because
64
+ * before a package is read there is nothing else to go on. `basePlatform` when none does, so the
65
+ * caller needs no guard. */
66
+ platformFor(dir) {
67
+ return this.platforms.first(p => (p.manifestProvider.read(dir) ? p : undefined)) ?? basePlatform;
68
+ }
69
+ /**
70
+ * **Not a constructor field, and that is forced by the order things happen in.** Plugins are what
71
+ * *find* the packages - `Repository.create` loads them before it calls `Workspace.resolve` - so
72
+ * the application has to exist, and be handed to every `init`, while there is still no
73
+ * repository to put in it.
74
+ *
75
+ * A throwing getter rather than `undefined`: a plugin reaching for packages during `init` has
76
+ * made a real mistake, and `undefined` would let it write a check that silently does nothing.
77
+ */
78
+ get repository() {
79
+ if (!this._repository) {
80
+ throw new Error('The repository is not available yet - plugins are loaded before the packages they are ' +
81
+ "what finds. Ask for it from a command or a service, not from a plugin's init().");
82
+ }
83
+ return this._repository;
84
+ }
85
+ /** `info` until `--log-level` or `.rmanrc "logLevel"` is resolved - which cannot happen here,
86
+ * since reading the config is itself work the application does. */
87
+ constructor(options) {
88
+ this.logger = new Logger(options?.logLevel ?? 'info');
89
+ registerCoreServices(this);
90
+ registerCoreTargets(this);
91
+ }
92
+ /**
93
+ * The single instance of a service, built on first use.
94
+ *
95
+ * **Lazy, and for two reasons.** `rman info` has no business constructing the changelog, version
96
+ * and release services, which is the same laziness the config scope's `git` getter was measured
97
+ * to need (0 git reads with property descriptors, 1 with a spread). And services call each other -
98
+ * `VersionService` reaches for `RunService` and `ChangelogService`, which reaches for
99
+ * `ChangeHashService` - so resolving at call time is what keeps that from being a construction
100
+ * cycle.
101
+ */
102
+ getService(name) {
103
+ const existing = this.services.get(name);
104
+ if (existing)
105
+ return existing;
106
+ const factory = this.factories.get(name);
107
+ if (!factory)
108
+ throw new Error(`No service registered under "${String(name)}"`);
109
+ const service = factory(this);
110
+ this.services.set(name, service);
111
+ return service;
112
+ }
113
+ /**
114
+ * Registers how a service is built. A plugin replacing one of the core's - a version planner that
115
+ * knows its own ecosystem's dependency ranges - does it here, which is why that no longer needs a
116
+ * seam of its own on the plugin interface.
117
+ *
118
+ * Refused once the service has been built: something is already holding the old instance, and
119
+ * swapping the factory then would leave two answers to one question in play.
120
+ */
121
+ setService(name, factory) {
122
+ if (this.services.has(name)) {
123
+ throw new Error(`Service "${String(name)}" has already been built and cannot be replaced`);
124
+ }
125
+ this.factories.set(name, factory);
126
+ }
127
+ /**
128
+ * Called by `Repository.create`, after the plugins that find the packages have run.
129
+ *
130
+ * **One application, one repository**, and a second is refused. It was briefly "last one wins",
131
+ * because a single shared application made two repositories in one test collide; each
132
+ * `Repository.create` makes its own now, so the rule holds again and nothing in a process is
133
+ * shared by accident.
134
+ */
135
+ attachRepository(repository) {
136
+ if (this._repository)
137
+ throw new Error('This application already has a repository');
138
+ this._repository = repository;
139
+ }
140
+ _repository;
141
+ services = new Map();
142
+ factories = new Map();
143
+ }
@@ -0,0 +1,14 @@
1
+ import type { CommandModule } from 'yargs';
2
+ import type { RmanConfig } from '../interfaces/rman-config.interface.js';
3
+ /**
4
+ * Turns a command's **declaration** into the yargs registration it describes.
5
+ *
6
+ * This is the one place that knows how the two relate, which is the point of declaring commands
7
+ * instead of building them: a command says what it has, and only this function says what yargs is
8
+ * told. Everything a `builder` used to do by hand - options, positionals, examples, parser
9
+ * switches - arrives as data now, so a typo in any of it is a compile error at the command rather
10
+ * than a flag that silently never existed.
11
+ */
12
+ export declare function toYargsCommand(meta: RmanConfig.CommandMetadata): CommandModule;
13
+ /** A command's own name - the first word of its `command` string, before any positional. */
14
+ export declare function commandName(command: string): string;