rman 1.3.0 → 2.0.0-beta.3

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 (187) hide show
  1. package/README.md +65 -31
  2. package/builtins/platforms/node/augmentation/rmanrc.augmentation.d.ts +83 -0
  3. package/builtins/platforms/node/augmentation/system-info.augmentation.d.ts +26 -0
  4. package/builtins/platforms/node/augmentation/system-info.augmentation.js +79 -0
  5. package/builtins/platforms/node/commands/ci.command.d.ts +133 -0
  6. package/builtins/platforms/node/commands/ci.command.js +60 -0
  7. package/builtins/platforms/node/commands/clean.command.d.ts +185 -0
  8. package/builtins/platforms/node/commands/clean.command.js +74 -0
  9. package/builtins/platforms/node/index.d.ts +8 -0
  10. package/builtins/platforms/node/index.js +8 -0
  11. package/builtins/platforms/node/node-config.interface.d.ts +76 -0
  12. package/builtins/platforms/node/node-config.interface.js +7 -0
  13. package/builtins/platforms/node/node-manifest.provider.d.ts +68 -0
  14. package/builtins/platforms/node/node-manifest.provider.js +125 -0
  15. package/builtins/platforms/node/node.platform.d.ts +53 -0
  16. package/builtins/platforms/node/node.platform.js +134 -0
  17. package/builtins/platforms/node/services/ci.service.d.ts +47 -0
  18. package/builtins/platforms/node/services/ci.service.js +213 -0
  19. package/builtins/platforms/node/services/clean.service.d.ts +53 -0
  20. package/builtins/platforms/node/services/clean.service.js +237 -0
  21. package/builtins/platforms/node/services/publish.service.d.ts +114 -0
  22. package/builtins/platforms/node/services/publish.service.js +371 -0
  23. package/builtins/platforms/node/services/version-plan.service.d.ts +44 -0
  24. package/builtins/platforms/node/services/version-plan.service.js +58 -0
  25. package/builtins/platforms/node/utils/workspace-range.d.ts +26 -0
  26. package/builtins/platforms/node/utils/workspace-range.js +28 -0
  27. package/builtins/presets/index.d.ts +15 -0
  28. package/builtins/presets/index.js +57 -0
  29. package/builtins/presets/node.d.ts +16 -0
  30. package/builtins/presets/node.js +57 -0
  31. package/{services → builtins/publish-targets/docker}/docker-publish.service.d.ts +52 -31
  32. package/{services → builtins/publish-targets/docker}/docker-publish.service.js +49 -26
  33. package/builtins/publish-targets/docker/docker.target.d.ts +54 -0
  34. package/builtins/publish-targets/docker/docker.target.js +40 -0
  35. package/builtins/publish-targets/docker/index.d.ts +2 -0
  36. package/builtins/publish-targets/docker/index.js +2 -0
  37. package/builtins/publish-targets/npm/index.d.ts +2 -0
  38. package/builtins/publish-targets/npm/index.js +2 -0
  39. package/builtins/publish-targets/npm/npm-publish-target.d.ts +73 -0
  40. package/builtins/publish-targets/npm/npm-publish-target.js +95 -0
  41. package/builtins/publish-targets/npm/npm-view.d.ts +48 -0
  42. package/builtins/publish-targets/npm/npm-view.js +71 -0
  43. package/cli.d.ts +8 -0
  44. package/cli.js +228 -93
  45. package/commands/build.command.d.ts +177 -3
  46. package/commands/build.command.js +20 -10
  47. package/commands/changelog.command.d.ts +296 -3
  48. package/commands/changelog.command.js +127 -43
  49. package/commands/config.command.d.ts +44 -3
  50. package/commands/config.command.js +53 -24
  51. package/commands/diff.command.d.ts +37 -3
  52. package/commands/diff.command.js +25 -16
  53. package/commands/exec.command.d.ts +193 -3
  54. package/commands/exec.command.js +60 -58
  55. package/commands/github-release.command.d.ts +154 -3
  56. package/commands/github-release.command.js +67 -37
  57. package/commands/import.command.d.ts +36 -3
  58. package/commands/import.command.js +28 -20
  59. package/commands/info.command.d.ts +35 -7
  60. package/commands/info.command.js +36 -30
  61. package/commands/lint.command.d.ts +176 -0
  62. package/commands/lint.command.js +34 -0
  63. package/commands/list.command.d.ts +163 -3
  64. package/commands/list.command.js +109 -71
  65. package/commands/publish.command.d.ts +231 -0
  66. package/commands/publish.command.js +304 -0
  67. package/commands/run.command.d.ts +186 -6
  68. package/commands/run.command.js +26 -72
  69. package/commands/test.command.d.ts +173 -3
  70. package/commands/test.command.js +16 -10
  71. package/commands/version.command.d.ts +330 -3
  72. package/commands/version.command.js +292 -78
  73. package/commands.d.ts +32 -0
  74. package/commands.js +28 -0
  75. package/constants.js +1 -1
  76. package/core/application.d.ts +116 -0
  77. package/core/application.js +160 -0
  78. package/core/classes/package.d.ts +219 -0
  79. package/core/classes/package.js +214 -0
  80. package/core/classes/registry.d.ts +30 -0
  81. package/core/classes/registry.js +47 -0
  82. package/core/{repository.d.ts → classes/repository.d.ts} +69 -20
  83. package/core/{repository.js → classes/repository.js} +195 -75
  84. package/core/classes/service.d.ts +49 -0
  85. package/core/classes/service.js +40 -0
  86. package/core/{version-scheme.d.ts → classes/version-scheme.d.ts} +23 -1
  87. package/core/{version-scheme.js → classes/version-scheme.js} +29 -1
  88. package/core/classes/workspace.d.ts +275 -0
  89. package/core/classes/workspace.js +537 -0
  90. package/core/command-builder.d.ts +14 -0
  91. package/core/command-builder.js +94 -0
  92. package/core/config/config-file-scope.d.ts +107 -0
  93. package/core/config/config-file-scope.js +226 -0
  94. package/core/config/config-interpolator.d.ts +164 -0
  95. package/core/config/config-interpolator.js +577 -0
  96. package/core/config/config-paths.d.ts +65 -0
  97. package/core/config/config-paths.js +78 -0
  98. package/core/config/config-reader.d.ts +192 -0
  99. package/core/config/config-reader.js +727 -0
  100. package/core/{merge-config.d.ts → config/merge-config.d.ts} +33 -34
  101. package/core/config/merge-config.js +260 -0
  102. package/core/{resolve-target.d.ts → config/resolve-config-target.d.ts} +1 -1
  103. package/core/{resolve-target.js → config/resolve-config-target.js} +1 -1
  104. package/core/{custom-command.d.ts → interfaces/custom-command.d.ts} +46 -10
  105. package/core/{custom-command.js → interfaces/custom-command.js} +44 -17
  106. package/core/{manifest.d.ts → interfaces/manifest.d.ts} +49 -27
  107. package/core/interfaces/manifest.js +132 -0
  108. package/core/interfaces/plugin.d.ts +174 -0
  109. package/core/interfaces/plugin.js +124 -0
  110. package/core/interfaces/publish-target.d.ts +136 -0
  111. package/core/interfaces/publish-target.js +43 -0
  112. package/core/{run-step.d.ts → interfaces/run-step.d.ts} +4 -4
  113. package/core/interfaces/run-step.js +1 -0
  114. package/core/plugin-loader.d.ts +25 -0
  115. package/core/plugin-loader.js +53 -0
  116. package/index.d.ts +122 -24
  117. package/index.js +66 -16
  118. package/interfaces/config-scope.interface.d.ts +223 -0
  119. package/interfaces/config-scope.interface.js +1 -0
  120. package/interfaces/rman-config.interface.d.ts +821 -203
  121. package/interfaces/rman-config.interface.js +78 -1
  122. package/package.json +3 -1
  123. package/services/change-hash.service.d.ts +42 -8
  124. package/services/change-hash.service.js +92 -13
  125. package/services/changelog.service.d.ts +91 -52
  126. package/services/changelog.service.js +460 -74
  127. package/services/config-service.d.ts +8 -0
  128. package/services/config-service.js +3 -0
  129. package/services/exec.service.d.ts +23 -12
  130. package/services/exec.service.js +14 -9
  131. package/services/github-release.service.d.ts +45 -34
  132. package/services/github-release.service.js +15 -12
  133. package/services/import.service.d.ts +25 -14
  134. package/services/import.service.js +9 -5
  135. package/services/list.service.d.ts +67 -13
  136. package/services/list.service.js +62 -15
  137. package/services/run.service.d.ts +35 -22
  138. package/services/run.service.js +316 -233
  139. package/services/system-info.d.ts +1 -1
  140. package/services/version-plan.service.d.ts +35 -8
  141. package/services/version-plan.service.js +96 -36
  142. package/services/version.service.d.ts +34 -14
  143. package/services/version.service.js +35 -16
  144. package/services.d.ts +1 -1
  145. package/services.js +1 -1
  146. package/utils/bin-path.d.ts +6 -7
  147. package/utils/bin-path.js +7 -18
  148. package/utils/branch-guard.d.ts +30 -1
  149. package/utils/branch-guard.js +31 -0
  150. package/utils/color-yaml.d.ts +8 -0
  151. package/utils/color-yaml.js +86 -0
  152. package/utils/exec.d.ts +10 -0
  153. package/utils/exec.js +1 -1
  154. package/utils/git.d.ts +26 -0
  155. package/utils/git.js +65 -0
  156. package/utils/logger.d.ts +2 -2
  157. package/utils/logger.js +1 -1
  158. package/utils/package-filter.d.ts +135 -8
  159. package/utils/package-filter.js +204 -16
  160. package/utils/printable-config.d.ts +9 -1
  161. package/utils/printable-config.js +46 -1
  162. package/utils/progress-panel.d.ts +9 -0
  163. package/utils/progress-panel.js +23 -1
  164. package/utils/release-version.d.ts +1 -1
  165. package/utils/run-bin.d.ts +10 -0
  166. package/utils/run-bin.js +1 -1
  167. package/utils/run-options.d.ts +97 -0
  168. package/utils/run-options.js +81 -0
  169. package/utils/version-group.d.ts +31 -0
  170. package/utils/version-group.js +43 -0
  171. package/utils/version-stamp.d.ts +1 -1
  172. package/utils/version-stamp.js +1 -1
  173. package/commands/changed.command.d.ts +0 -3
  174. package/commands/changed.command.js +0 -29
  175. package/core/config.d.ts +0 -409
  176. package/core/config.js +0 -939
  177. package/core/extends-config.d.ts +0 -27
  178. package/core/extends-config.js +0 -89
  179. package/core/manifest.js +0 -150
  180. package/core/merge-config.js +0 -219
  181. package/core/package.d.ts +0 -83
  182. package/core/package.js +0 -106
  183. package/core/plugin.d.ts +0 -112
  184. package/core/plugin.js +0 -189
  185. package/core/workspace.d.ts +0 -68
  186. package/core/workspace.js +0 -83
  187. /package/{core/run-step.js → builtins/platforms/node/augmentation/rmanrc.augmentation.js} +0 -0
@@ -0,0 +1,136 @@
1
+ import type { RmanConfig } from '../../interfaces/rman-config.interface.js';
2
+ import type { PackageFilterOptions } from '../../utils/package-filter.js';
3
+ import type { RmanApplication } from '../application.js';
4
+ import type { Package } from '../classes/package.js';
5
+ import type { Repository } from '../classes/repository.js';
6
+ /**
7
+ * **Where a package's artifact ships, as a contribution.**
8
+ *
9
+ * `publish` used to be `rman-node`'s command, and that was backwards in a way worth stating: the
10
+ * command itself is about a *repository* - which packages are candidates, which versions are
11
+ * already out there, what order to push them in, what to print - and none of that is npm's. What is
12
+ * npm's is one answer to "is this version on the registry, and how do I push it", which is exactly
13
+ * the size of this interface. Docker publishing was in the core all along and could only be reached
14
+ * through a Node plugin's command; a Cargo repository could reach neither.
15
+ *
16
+ * So: the core ships `publish` and the `docker` target, the `node` built-in contributes `npm`, and a
17
+ * repository in any other ecosystem contributes its own without either package knowing about it.
18
+ *
19
+ * A target owns three things and no more:
20
+ *
21
+ * - **Which packages are its own by default** (`claims`) - see below.
22
+ * - **Its own CLI options**, merged into `publish`'s by the command. Every flag that only meant
23
+ * something to npm (`--access`, `--tag`, `--otp`, `--registry`, `--userconfig`, `--contents`,
24
+ * `--package-manager`) is declared here now instead of on a command the core would otherwise have
25
+ * to know them all in advance.
26
+ * - **`getPlan`/`applyPlan`** - the same two-step shape every other rman service uses, and the
27
+ * reason `--dry-run` and the confirmation prompt are the command's business rather than each
28
+ * target's.
29
+ */
30
+ export interface PublishTarget {
31
+ /** The name `publish.target` and `--target` use, and the label printed beside a package. */
32
+ readonly name: string;
33
+ /** One line for `--target`'s help - a reader asking `rman publish --help` in a polyglot
34
+ * repository has no other way to find out which targets are even installed. */
35
+ readonly describe?: string;
36
+ /**
37
+ * This target's own flags, merged into `publish`'s options.
38
+ *
39
+ * Flat, not namespaced: `--tag` reads better than `--npm-tag`, and two targets colliding on a
40
+ * name is refused loudly by the command rather than resolved by a rule nobody would remember.
41
+ * Name a flag for the target when it genuinely belongs to one (`--docker-namespace`).
42
+ */
43
+ readonly options?: Record<string, RmanConfig.CommandOption>;
44
+ /**
45
+ * The technologies this target is for, by `Platform.name` - `['node']` for `npm`. Absent means
46
+ * every platform, which is what a technology-agnostic target says: any package can carry a
47
+ * Dockerfile, so `docker` declares none.
48
+ *
49
+ * On its own it is the whole of `claims`: a package of one of these platforms that declares no
50
+ * `publish.target` ships here. With `claims` beside it, this narrows first and `claims` decides
51
+ * within.
52
+ *
53
+ * The same word `CommandMetadata.platforms` uses, and the same question.
54
+ */
55
+ readonly platforms?: string[];
56
+ /**
57
+ * Whether a package that declares **no** `publish.target` at all ships here.
58
+ *
59
+ * Absent means never - the target is opt-in, which is what `docker` is - unless `platforms` says
60
+ * otherwise, in which case that is the answer. Present, it decides for the packages `platforms`
61
+ * let through.
62
+ */
63
+ claims?(pkg: Package): boolean;
64
+ /** What this target *would* do - never publishes. Called even under `--dry-run`, which is the
65
+ * whole point of the split. */
66
+ getPlan(ctx: PublishTarget.Context): Promise<PublishTarget.Entry[]>;
67
+ /** Publishes every `'publish'` entry in `plan`, and returns what it did. */
68
+ applyPlan(ctx: PublishTarget.Context, plan: PublishTarget.Entry[]): Promise<PublishTarget.Entry[]>;
69
+ }
70
+ export declare namespace PublishTarget {
71
+ /**
72
+ * What a target is handed, once per call.
73
+ *
74
+ * `args` is the parsed argv, untyped on purpose: a target declared its own options, so it is the
75
+ * only thing that knows their names and types, and the core cannot be made to know them without
76
+ * becoming the thing this interface exists to stop it being.
77
+ */
78
+ interface Context {
79
+ readonly app: RmanApplication;
80
+ readonly repository: Repository;
81
+ /** The filters every target shares, read off argv once by the command. */
82
+ readonly options: Options;
83
+ /** The whole parsed argv - a target reads the options it declared out of this. */
84
+ readonly args: Record<string, any>;
85
+ }
86
+ interface Options extends PackageFilterOptions {
87
+ /** A package with uncommitted local changes is excluded (`'skip'`) instead of aborting the
88
+ * whole plan (`'error'`). */
89
+ ignoreDirty?: boolean;
90
+ }
91
+ /**
92
+ * One package's outcome for one target.
93
+ *
94
+ * `'skip'` and `'error'` differ in what they do to the run: an error aborts before anything is
95
+ * published, a skip is a package the plan deliberately leaves alone. A target may add fields of
96
+ * its own (docker carries the resolved image reference); `detail` is the one the command prints,
97
+ * so whatever a target wants shown beside a package goes there.
98
+ */
99
+ interface Entry {
100
+ package: Package;
101
+ version: string;
102
+ status: 'publish' | 'skip' | 'up-to-date' | 'error';
103
+ /** Printed after the package name - docker's resolved `<namespace>/<image>`, for instance. */
104
+ detail?: string;
105
+ reason?: string;
106
+ }
107
+ }
108
+ /**
109
+ * **A target's name, and deliberately not a union.**
110
+ *
111
+ * It was `'npm' | 'docker'`, the type half of a bug whose runtime half was a hardcoded `['npm']`
112
+ * default, so `rman list --json` reported `publishTargets: ["npm"]` for a Cargo package. Both are
113
+ * gone together - which targets exist is whatever the repository's plugins contribute
114
+ * (`RmanApplication.publishTargets`), so a union here would mean the core naming plugins it cannot
115
+ * know about, exactly as `Package.provider` must not.
116
+ *
117
+ * A name nothing implements is caught where the facts are, by `publish` itself, naming the targets
118
+ * this repository does have.
119
+ */
120
+ export type PublishTargetName = string;
121
+ /**
122
+ * The targets a package **declares**, or `undefined` when it declares none - which is not the same
123
+ * as an empty list, and the difference is what `claims` is asked about.
124
+ */
125
+ export declare function declaredTargets(pkg: Package): string[] | undefined;
126
+ /**
127
+ * Whether `pkg` ships to `target`: its own `publish.target` decides when it has one, and the
128
+ * target's `platforms`/`claims` decide when it does not.
129
+ */
130
+ export declare function shipsTo(pkg: Package, target: PublishTarget): boolean;
131
+ /** Every registered target `pkg` ships to, in registration order. The single answer to "where does
132
+ * this package go", so `publish` and `list --json` cannot disagree about it. */
133
+ export declare function targetsOf(app: RmanApplication, pkg: Package): PublishTarget[];
134
+ /** Names in a package's `publish.target` that no registered target answers to - a misconfiguration
135
+ * that used to be caught by a `choices: ['npm', 'docker']` the core can no longer write down. */
136
+ export declare function unknownTargets(app: RmanApplication, pkg: Package): string[];
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The targets a package **declares**, or `undefined` when it declares none - which is not the same
3
+ * as an empty list, and the difference is what `claims` is asked about.
4
+ */
5
+ export function declaredTargets(pkg) {
6
+ const declared = pkg.config.publish?.target;
7
+ if (declared === undefined)
8
+ return undefined;
9
+ return Array.isArray(declared) ? [...declared] : [declared];
10
+ }
11
+ /**
12
+ * Whether `pkg` ships to `target`: its own `publish.target` decides when it has one, and the
13
+ * target's `platforms`/`claims` decide when it does not.
14
+ */
15
+ /* **A declared `publish.target` is the last word**, and neither `platforms` nor `claims` is asked
16
+ * after it - a package naming a target has said where it goes, whatever ecosystem it is. Narrowing
17
+ * that would turn a default into a rule about who may say what. */
18
+ export function shipsTo(pkg, target) {
19
+ const declared = declaredTargets(pkg);
20
+ if (declared)
21
+ return declared.includes(target.name);
22
+ /** `platforms` narrows first; `claims` decides within what it let through. With only `platforms`,
23
+ * belonging to one of them *is* the claim; with neither, the target is opt-in. */
24
+ if (target.platforms?.length && !target.platforms.includes(pkg.provider))
25
+ return false;
26
+ if (target.claims)
27
+ return target.claims(pkg);
28
+ return !!target.platforms?.length;
29
+ }
30
+ /** Every registered target `pkg` ships to, in registration order. The single answer to "where does
31
+ * this package go", so `publish` and `list --json` cannot disagree about it. */
32
+ export function targetsOf(app, pkg) {
33
+ return app.publishTargets.all.filter(target => shipsTo(pkg, target));
34
+ }
35
+ /** Names in a package's `publish.target` that no registered target answers to - a misconfiguration
36
+ * that used to be caught by a `choices: ['npm', 'docker']` the core can no longer write down. */
37
+ export function unknownTargets(app, pkg) {
38
+ const declared = declaredTargets(pkg);
39
+ if (!declared)
40
+ return [];
41
+ const known = new Set(app.publishTargets.all.map(t => t.name));
42
+ return declared.filter(name => !known.has(name));
43
+ }
@@ -1,7 +1,7 @@
1
- import type { Logger } from '../utils/logger.js';
2
- import type { RunBinOptions, RunBinResult } from '../utils/run-bin.js';
3
- import type { Package } from './package.js';
4
- import type { Repository } from './repository.js';
1
+ import type { Logger } from '../../utils/logger.js';
2
+ import type { RunBinOptions, RunBinResult } from '../../utils/run-bin.js';
3
+ import type { Package } from '../classes/package.js';
4
+ import type { Repository } from '../classes/repository.js';
5
5
  /**
6
6
  * What a **function step** is handed - a `run.<script>.before`/`.exec`/`.after` or a
7
7
  * `version.<slot>` written as JavaScript instead of a shell command, and the `if` deciding whether
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import type { RmanApplication } from './application.js';
2
+ import { type Platform, type Plugin } from './interfaces/plugin.js';
3
+ /** The `.rmanrc` key naming plugin packages to load. */
4
+ export declare const PLUGINS_KEY = "plugins";
5
+ /**
6
+ * **Puts one contribution onto an application** - a technology, or a plugin - and the only place
7
+ * that knows how.
8
+ *
9
+ * **Exported because a spec must not reimplement it.** A fixture writing the registrations out by
10
+ * hand is a second implementation that drifts, which is exactly how a fixture ends up proving the
11
+ * core works when it does not. A spec brings its own technology through this, the same door a
12
+ * config's does.
13
+ *
14
+ * **Neither hook is called here, and that is deliberate.** `afterInitApplication` and
15
+ * `afterInitRepository` belong to `Repository.create`, which knows when each stage is reached and
16
+ * can await them; this is a synchronous call a fixture makes while building an application.
17
+ */
18
+ export declare function registerPlugin(app: RmanApplication, entry: Plugin | Platform): void;
19
+ export declare function checkCustomCommand<T extends {
20
+ command?: string;
21
+ describe?: unknown;
22
+ handler?: unknown;
23
+ }>(command: T, pluginName: string): T & {
24
+ command: string;
25
+ };
@@ -0,0 +1,53 @@
1
+ import { isPlatform } from './interfaces/plugin.js';
2
+ /** The `.rmanrc` key naming plugin packages to load. */
3
+ export const PLUGINS_KEY = 'plugins';
4
+ /**
5
+ * **Puts one contribution onto an application** - a technology, or a plugin - and the only place
6
+ * that knows how.
7
+ *
8
+ * **Exported because a spec must not reimplement it.** A fixture writing the registrations out by
9
+ * hand is a second implementation that drifts, which is exactly how a fixture ends up proving the
10
+ * core works when it does not. A spec brings its own technology through this, the same door a
11
+ * config's does.
12
+ *
13
+ * **Neither hook is called here, and that is deliberate.** `afterInitApplication` and
14
+ * `afterInitRepository` belong to `Repository.create`, which knows when each stage is reached and
15
+ * can await them; this is a synchronous call a fixture makes while building an application.
16
+ */
17
+ /* **Two registries and no normalization, which is what the split bought.** A bare `Platform` used
18
+ * to be wrapped in `{ name, platforms: [entry] }` so that everything downstream dealt in one shape
19
+ * - and that shape was `Plugin.platforms`, which is gone: `platforms` is a config key now, so a
20
+ * package shipping a technology declares it there and nothing has to invent a plugin around it.
21
+ * What is left is a question with two honest answers, asked once. */
22
+ export function registerPlugin(app, entry) {
23
+ if (isPlatform(entry)) {
24
+ app.platforms.add(entry);
25
+ /**
26
+ * **The orchestrator, and only that.** A plan is computed for the whole repository at once -
27
+ * groups span packages, the ripple crosses them - so one planner drives the traversal and the
28
+ * last registration wins it.
29
+ *
30
+ * The two decisions that belong to a *technology* are not taken from here: `detectBoundary`
31
+ * and `cascade` are asked of `pkg.platform.versionPlanner` per package, which is why a platform
32
+ * still declares one even when it is not the last to register.
33
+ */
34
+ if (entry.versionPlanner)
35
+ app.versionPlanner = entry.versionPlanner;
36
+ return;
37
+ }
38
+ app.plugins.add(entry);
39
+ }
40
+ export function checkCustomCommand(command, pluginName) {
41
+ const declared = command?.command?.trim();
42
+ if (!declared) {
43
+ throw new Error(`Plugin "${pluginName}" has a command with no "command" name - it cannot be registered.`);
44
+ }
45
+ if (typeof command.handler !== 'function') {
46
+ throw new Error(`Plugin "${pluginName}" command "${declared}" has no "handler" function.`);
47
+ }
48
+ if (typeof command.describe !== 'string' || !command.describe) {
49
+ throw new Error(`Plugin "${pluginName}" command "${declared}" has no "describe" - \`rman --help\` would have ` +
50
+ `nothing to list it by.`);
51
+ }
52
+ return { ...command, command: declared };
53
+ }
package/index.d.ts CHANGED
@@ -6,44 +6,121 @@
6
6
  * per domain - CLI-only concerns (argv parsing, `--help` text, and all console/file presentation)
7
7
  * stay in `cli.ts` and the individual `commands/*.command.ts` modules, which are not exported here.
8
8
  *
9
- * **This is also the plugin contract.** A plugin (`rman-node`, say) is an ordinary package that
10
- * imports from here, so everything a command needs in order to live outside rman has to be
11
- * exported - the progress panel, the package filter, the branch guard, the git helper. What is
9
+ * **This is also the plugin contract.** A third-party plugin is an ordinary package that imports
10
+ * from here, so everything a command needs in order to live outside rman has to be exported - the progress panel, the package filter, the branch guard, the git helper. What is
12
11
  * *not* exported is deliberately private: config resolution internals, the expression evaluator,
13
12
  * the command registry.
14
13
  */
15
- export { defineConfig } from './core/config.js';
16
- export type { CommandContext, CustomCommand } from './core/custom-command.js';
17
- export { defineCommand } from './core/custom-command.js';
18
- /** The manifest seam: where a package's name and version are written, and how it is numbered.
19
- * The core has no provider - `package.json` is npm's answer, and lives in `rman-node`. */
20
- export type { ManifestProvider } from './core/manifest.js';
14
+ /**
15
+ * **The `node` built-in's type augmentation, imported for the same reason `commands.ts` is.**
16
+ *
17
+ * A `declare module` augmentation applies only where the module declaring it is part of the
18
+ * program. The node plugin's lives in `plugins/node/augmentation/rman.augmentation.ts` and is
19
+ * imported by the *plugin's* entry point - which nothing here reached, so `clean`, `publish.npm`
20
+ * and the rest existed for rman itself and for nobody else.
21
+ *
22
+ * **Measured on a real consumer**, `@panates/rman-node`, a config package annotated with these
23
+ * types: `Object literal may only specify known properties, and 'clean' does not exist in type
24
+ * 'RmanConfig'`, plus `Property 'npm' does not exist` - ten errors across its config and its own
25
+ * suite. It is the identical failure the core's command keys caused when they stopped being
26
+ * hand-written centrally, and it reappeared the moment the plugin moved *inside* rman: until then
27
+ * a consumer imported `rman-node` and got the augmentation with the package.
28
+ *
29
+ * Type-only, so the emitted module is empty - imported for what it declares, not for what it does.
30
+ * Pinned in `docs-api.spec.ts`, which imports only from this file.
31
+ */
32
+ import './builtins/platforms/node/augmentation/rmanrc.augmentation.js';
33
+ import type { RmanConfig as CommandDeclaration } from './interfaces/rman-config.interface.js';
34
+ export { defineConfig } from './interfaces/rman-config.interface.js';
35
+ /**
36
+ * **A config value written as a function**, and the scope it is handed. Exported because a config
37
+ * author could not name either: `interpolateConfig` calls a function wherever a `${{ }}` could
38
+ * stand, but the only function form the types admitted was a *step* - so the primary spelling of
39
+ * `value` was unexpressible.
40
+ *
41
+ * `ConfigValueContext` is what a value function receives; `ConfigScope` is the same thing without
42
+ * `value`, i.e. what an expression sees. Not to be confused with `RunStepContext`, which is what a
43
+ * step gets, later, with a working directory and a `runBin`.
44
+ */
45
+ export type { CommandContext, CustomCommand } from './core/interfaces/custom-command.js';
46
+ export { defineCommand } from './core/interfaces/custom-command.js';
47
+ export type { ConfigScope, ConfigValueContext, FileScope, PackageScope } from './interfaces/config-scope.interface.js';
48
+ export type { ConfigValue, Resolved, ResolvedConfig } from './interfaces/rman-config.interface.js';
21
49
  /** Both the shape and the registry: `const m: Manifest` and `Manifest.read(dir)` - merged onto one
22
50
  * name so a plugin can augment it the way it augments `SystemInfo`. */
23
- export { Manifest } from './core/manifest.js';
24
- export { Package } from './core/package.js';
25
- export type { RmanPlugin } from './core/plugin.js';
26
- export { definePlugin } from './core/plugin.js';
27
- export { Repository } from './core/repository.js';
28
- export type { RunConditionFn, RunStepContext, RunStepFn, RunStepValue } from './core/run-step.js';
29
- export type { ChangeKind } from './core/version-scheme.js';
51
+ export { RmanApplication } from './core/application.js';
52
+ /** The manifest seam, grouped on a plugin as `Plugin.manifestProvider`: where a package's name and
53
+ * version are written, what it declares, and how it is numbered and stamped. The core has none -
54
+ * `package.json` is npm's answer, and belongs to the `node` built-in. */
55
+ export { Package } from './core/classes/package.js';
56
+ export type { ManifestProvider } from './core/interfaces/manifest.js';
57
+ export { Manifest } from './core/interfaces/manifest.js';
58
+ /** The publish seam: where a package's artifact ships. The core brings `docker` (nobody's
59
+ * ecosystem); npm's target is the `node` built-in's, and any other technology's is its own plugin's. */
60
+ export { Registry } from './core/classes/registry.js';
61
+ export { Repository } from './core/classes/repository.js';
62
+ export { Service, type ServiceFactory, type ServiceMap } from './core/classes/service.js';
63
+ export { declaredTargets, type PublishTarget, shipsTo, targetsOf, unknownTargets, } from './core/interfaces/publish-target.js';
64
+ export type { RunConditionFn, RunStepContext, RunStepFn, RunStepValue } from './core/interfaces/run-step.js';
65
+ /**
66
+ * **`Platform` is one technology, whole**; **`Plugin` is whatever a package contributes**, platforms
67
+ * among them.
68
+ *
69
+ * A platform says how its packages are recognized and written, where they live, what goes on a
70
+ * child's PATH and how its releases are planned - `manifestProvider` is what makes one, and it is
71
+ * required. A plugin carries `platforms` and an `init` for anything the seams do not name yet; a
72
+ * bare `Platform` is accepted wherever a `Plugin` is, as sugar for the plugin that provides only it.
73
+ *
74
+ * **Both must be declared through their factory.** `loadPlugins` checks for the mark, because an
75
+ * rman 1.x plugin was `{ name, init }` and so is a 2.x plugin contributing nothing but an `init` -
76
+ * no shape test can tell them apart.
77
+ */
78
+ export type { ChangeKind } from './core/classes/version-scheme.js';
79
+ export { basePlatform, definePlatform, definePlugin, isPlatform, type Platform, type Plugin, type PluginContext, } from './core/interfaces/plugin.js';
30
80
  /** The numbering seam. `VersionScheme` is abstract - `highestVersion`/`highestBump`/`smallestBump`
31
81
  * are implemented from the members around them, so a scheme states only what it must and still
32
82
  * overrides any of the three. `SemverScheme` is exported to subclass rather than restate. */
33
- export { assertOneScheme, SemverScheme, semverScheme, VersionScheme } from './core/version-scheme.js';
83
+ export { assertOneScheme, SemverScheme, semverScheme, VersionScheme } from './core/classes/version-scheme.js';
34
84
  /** The workspace seam: how a repository's packages are found. A plugin contributes a provider
35
- * (see `RmanPlugin.workspace`); the core has none, so `workspaces` is npm's idea and lives in
36
- * `rman-node`. */
85
+ * (see `Plugin.workspace`); the core has none, so `workspaces` is npm's idea and belongs to the
86
+ * `node` built-in. */
37
87
  /** `Workspace.Layout`, `Workspace.Provider`, `Workspace.addProvider`, `Workspace.resolve`,
38
88
  * `Workspace.findRoot` - one namespace, so a plugin can augment it. */
39
- export { Workspace } from './core/workspace.js';
89
+ export { Workspace } from './core/classes/workspace.js';
90
+ /**
91
+ * **How a command is declared** - the same API the built-ins use, so a plugin's command is declared
92
+ * rather than built: options as data (checked for typos), positionals named against the command
93
+ * string, `--config` keys derived from what the command owns, and `ArgsOf` for the handler.
94
+ *
95
+ * `declareCommand`, not `registerCommand`: the latter pushes onto a module-level registry every
96
+ * `runCli` walks, so a plugin using it would give its commands to repositories that never named it.
97
+ * Anything else puts the function in its config's `commands`.
98
+ *
99
+ * **Flat names rather than `RmanConfig.CommandOption`**, and they outlived the reason they were
100
+ * introduced: a second file exported a `RmanConfig` too, so the namespace holding these was
101
+ * unreachable from outside the package. The two are one file now and `RmanConfig` *is* exported -
102
+ * these stay because they are the better names for the job. A plugin author declaring a flag wants
103
+ * `CommandOption`; the config it happens to contribute to is not what they are naming.
104
+ */
105
+ export { declareCommand } from './interfaces/rman-config.interface.js';
106
+ export type CommandOption = CommandDeclaration.CommandOption;
107
+ /** One declared positional, for the same reason `CommandOption` is here: it is the other half of a
108
+ * command's surface, and it was the half a plugin could not name without importing yargs. */
109
+ export type PositionalOption = CommandDeclaration.PositionalOption;
110
+ export type CommandMetadata = CommandDeclaration.CommandMetadata;
111
+ export type CommandRegisterFunction = CommandDeclaration.CommandRegisterFunction;
112
+ /** The argv a command's handler is annotated with - see `RmanConfig.ArgsOf` for why it is annotated
113
+ * rather than inferred. */
114
+ export type ArgsOf<C, Cmd extends string> = CommandDeclaration.ArgsOf<C, Cmd>;
115
+ export type GlobalArgs = CommandDeclaration.GlobalArgs;
116
+ export * from './commands.js';
40
117
  export * from './interfaces/rman-config.interface.js';
41
118
  export * from './services.js';
42
119
  /** Branch guarding: `allowBranch`/`ignoreBranch`, so a plugin's release command refuses to run on
43
120
  * the wrong branch exactly as `publish` and `version` do. */
44
- export { applyBranchGuardOptions, assertAllowedBranch, type BranchGuardOptions, readBranchGuardOptions, } from './utils/branch-guard.js';
121
+ export { applyBranchGuardOptions, assertAllowedBranch, type BranchGuardOptions, branchGuardOptions, readBranchGuardOptions, } from './utils/branch-guard.js';
45
122
  /** Where a repository's locally installed binaries live - the core spells the PATH variable, a
46
- * plugin says which directories go on it (see `RmanPlugin.binPaths`). */
123
+ * plugin says which directories go on it (see `Plugin.binPaths`). */
47
124
  export { BinPath } from './utils/bin-path.js';
48
125
  /** A shell command, with the repository's local binaries on PATH - for a command string an author
49
126
  * wrote. `runBin` is the one to reach for when the arguments are assembled in code. */
@@ -54,13 +131,34 @@ export type { LogLevel } from './utils/logger.js';
54
131
  export { LOG_LEVELS, Logger, resolveRootLogLevel } from './utils/logger.js';
55
132
  /** `--scope`/`--deps`/`--dependents`/`--private`, so a plugin's command filters packages the same
56
133
  * way every built-in does rather than inventing its own flags. */
57
- export { applyPackageFilterOptions, applyRootOption, filterPackages, type PackageFilterOptions, readPackageFilterOptions, readRootOption, } from './utils/package-filter.js';
134
+ export { applyFromRootOption, applyPackageFilterOptions, filterPackages, fromRootOption, type PackageFilterOptions, packageFilterOptions, readFromRootOption, readPackageFilterOptions, ROOT_SELECTOR, } from './utils/package-filter.js';
58
135
  /** The live panel `run`/`build`/`clean` print - a plugin's per-package command looks like the rest
59
136
  * of rman instead of like a script someone bolted on. */
60
137
  export { formatDuration, type ProgressItem, ProgressPanel, type ProgressStatus, type ProgressSummary, } from './utils/progress-panel.js';
61
- /** Version stamping helpers a `ManifestProvider.stampVersion` can delegate to - the quoted-constant
138
+ /** Version stamping helpers a `Plugin.stampVersion` can delegate to - the quoted-constant
62
139
  * pattern most languages share, and the OCI Dockerfile label (which `version` stamps itself, since
63
140
  * the label's value is by specification the package's version). */
141
+ /** A calendar version's time part (`2026.9.15-1430`) is a semver *prerelease identifier* by
142
+ * construction, so anything asking "is this a preview?" has to rule it out first - `github-release`
143
+ * does, and so must a publish target deciding whether a version needs its own dist-tag. Exported
144
+ * because that second caller lives in a plugin. */
145
+ /**
146
+ * **The `node` preset's own surface.** It ships inside rman rather than as `rman-node`, so its
147
+ * services and target are named from here - a repository gets the technology from
148
+ * `DEFAULT_PRESETS`, or names it with `extends: "rman:node"`, and never constructs any of this by
149
+ * hand.
150
+ *
151
+ * Its `.rmanrc` *keys* arrive separately, through the bare import at the top of this file - see
152
+ * there for why that import is not tidiness.
153
+ */
154
+ export type { NodeConfigKeys, RmanNodeConfig } from './builtins/platforms/node/node-config.interface.js';
155
+ export { CiService } from './builtins/platforms/node/services/ci.service.js';
156
+ export { CleanService } from './builtins/platforms/node/services/clean.service.js';
157
+ export { PublishService } from './builtins/platforms/node/services/publish.service.js';
158
+ export { NodeVersionPlanService } from './builtins/platforms/node/services/version-plan.service.js';
159
+ export type { ParsedWorkspaceRange } from './builtins/platforms/node/utils/workspace-range.js';
160
+ export { NPM_TARGET, NpmPublishTarget } from './builtins/publish-targets/npm/npm-publish-target.js';
161
+ export { isCalendarVersion } from './utils/release-version.js';
64
162
  export type { RunBinOptions, RunBinResult } from './utils/run-bin.js';
65
163
  export { runBin } from './utils/run-bin.js';
66
164
  export { OCI_VERSION_LABEL, stampVersionConstant, stampVersionLabel } from './utils/version-stamp.js';
package/index.js CHANGED
@@ -6,47 +6,97 @@
6
6
  * per domain - CLI-only concerns (argv parsing, `--help` text, and all console/file presentation)
7
7
  * stay in `cli.ts` and the individual `commands/*.command.ts` modules, which are not exported here.
8
8
  *
9
- * **This is also the plugin contract.** A plugin (`rman-node`, say) is an ordinary package that
10
- * imports from here, so everything a command needs in order to live outside rman has to be
11
- * exported - the progress panel, the package filter, the branch guard, the git helper. What is
9
+ * **This is also the plugin contract.** A third-party plugin is an ordinary package that imports
10
+ * from here, so everything a command needs in order to live outside rman has to be exported - the progress panel, the package filter, the branch guard, the git helper. What is
12
11
  * *not* exported is deliberately private: config resolution internals, the expression evaluator,
13
12
  * the command registry.
14
13
  */
15
- export { defineConfig } from './core/config.js';
16
- export { defineCommand } from './core/custom-command.js';
14
+ /**
15
+ * **The `node` built-in's type augmentation, imported for the same reason `commands.ts` is.**
16
+ *
17
+ * A `declare module` augmentation applies only where the module declaring it is part of the
18
+ * program. The node plugin's lives in `plugins/node/augmentation/rman.augmentation.ts` and is
19
+ * imported by the *plugin's* entry point - which nothing here reached, so `clean`, `publish.npm`
20
+ * and the rest existed for rman itself and for nobody else.
21
+ *
22
+ * **Measured on a real consumer**, `@panates/rman-node`, a config package annotated with these
23
+ * types: `Object literal may only specify known properties, and 'clean' does not exist in type
24
+ * 'RmanConfig'`, plus `Property 'npm' does not exist` - ten errors across its config and its own
25
+ * suite. It is the identical failure the core's command keys caused when they stopped being
26
+ * hand-written centrally, and it reappeared the moment the plugin moved *inside* rman: until then
27
+ * a consumer imported `rman-node` and got the augmentation with the package.
28
+ *
29
+ * Type-only, so the emitted module is empty - imported for what it declares, not for what it does.
30
+ * Pinned in `docs-api.spec.ts`, which imports only from this file.
31
+ */
32
+ import './builtins/platforms/node/augmentation/rmanrc.augmentation.js';
33
+ export { defineConfig } from './interfaces/rman-config.interface.js';
34
+ export { defineCommand } from './core/interfaces/custom-command.js';
17
35
  /** Both the shape and the registry: `const m: Manifest` and `Manifest.read(dir)` - merged onto one
18
36
  * name so a plugin can augment it the way it augments `SystemInfo`. */
19
- export { Manifest } from './core/manifest.js';
20
- export { Package } from './core/package.js';
21
- export { definePlugin } from './core/plugin.js';
22
- export { Repository } from './core/repository.js';
37
+ export { RmanApplication } from './core/application.js';
38
+ /** The manifest seam, grouped on a plugin as `Plugin.manifestProvider`: where a package's name and
39
+ * version are written, what it declares, and how it is numbered and stamped. The core has none -
40
+ * `package.json` is npm's answer, and belongs to the `node` built-in. */
41
+ export { Package } from './core/classes/package.js';
42
+ export { Manifest } from './core/interfaces/manifest.js';
43
+ /** The publish seam: where a package's artifact ships. The core brings `docker` (nobody's
44
+ * ecosystem); npm's target is the `node` built-in's, and any other technology's is its own plugin's. */
45
+ export { Registry } from './core/classes/registry.js';
46
+ export { Repository } from './core/classes/repository.js';
47
+ export { Service } from './core/classes/service.js';
48
+ export { declaredTargets, shipsTo, targetsOf, unknownTargets, } from './core/interfaces/publish-target.js';
49
+ export { basePlatform, definePlatform, definePlugin, isPlatform, } from './core/interfaces/plugin.js';
23
50
  /** The numbering seam. `VersionScheme` is abstract - `highestVersion`/`highestBump`/`smallestBump`
24
51
  * are implemented from the members around them, so a scheme states only what it must and still
25
52
  * overrides any of the three. `SemverScheme` is exported to subclass rather than restate. */
26
- export { assertOneScheme, SemverScheme, semverScheme, VersionScheme } from './core/version-scheme.js';
53
+ export { assertOneScheme, SemverScheme, semverScheme, VersionScheme } from './core/classes/version-scheme.js';
27
54
  /** The workspace seam: how a repository's packages are found. A plugin contributes a provider
28
- * (see `RmanPlugin.workspace`); the core has none, so `workspaces` is npm's idea and lives in
29
- * `rman-node`. */
55
+ * (see `Plugin.workspace`); the core has none, so `workspaces` is npm's idea and belongs to the
56
+ * `node` built-in. */
30
57
  /** `Workspace.Layout`, `Workspace.Provider`, `Workspace.addProvider`, `Workspace.resolve`,
31
58
  * `Workspace.findRoot` - one namespace, so a plugin can augment it. */
32
- export { Workspace } from './core/workspace.js';
59
+ export { Workspace } from './core/classes/workspace.js';
60
+ /**
61
+ * **How a command is declared** - the same API the built-ins use, so a plugin's command is declared
62
+ * rather than built: options as data (checked for typos), positionals named against the command
63
+ * string, `--config` keys derived from what the command owns, and `ArgsOf` for the handler.
64
+ *
65
+ * `declareCommand`, not `registerCommand`: the latter pushes onto a module-level registry every
66
+ * `runCli` walks, so a plugin using it would give its commands to repositories that never named it.
67
+ * Anything else puts the function in its config's `commands`.
68
+ *
69
+ * **Flat names rather than `RmanConfig.CommandOption`**, and they outlived the reason they were
70
+ * introduced: a second file exported a `RmanConfig` too, so the namespace holding these was
71
+ * unreachable from outside the package. The two are one file now and `RmanConfig` *is* exported -
72
+ * these stay because they are the better names for the job. A plugin author declaring a flag wants
73
+ * `CommandOption`; the config it happens to contribute to is not what they are naming.
74
+ */
75
+ export { declareCommand } from './interfaces/rman-config.interface.js';
76
+ export * from './commands.js';
33
77
  export * from './interfaces/rman-config.interface.js';
34
78
  export * from './services.js';
35
79
  // --- what a command needs to behave like a built-in one -----------------------------------------
36
80
  /** Branch guarding: `allowBranch`/`ignoreBranch`, so a plugin's release command refuses to run on
37
81
  * the wrong branch exactly as `publish` and `version` do. */
38
- export { applyBranchGuardOptions, assertAllowedBranch, readBranchGuardOptions, } from './utils/branch-guard.js';
82
+ export { applyBranchGuardOptions, assertAllowedBranch, branchGuardOptions, readBranchGuardOptions, } from './utils/branch-guard.js';
39
83
  /** Where a repository's locally installed binaries live - the core spells the PATH variable, a
40
- * plugin says which directories go on it (see `RmanPlugin.binPaths`). */
84
+ * plugin says which directories go on it (see `Plugin.binPaths`). */
41
85
  export { BinPath } from './utils/bin-path.js';
42
86
  export { exec } from './utils/exec.js';
43
87
  export { GitHelper } from './utils/git.js';
44
88
  export { LOG_LEVELS, Logger, resolveRootLogLevel } from './utils/logger.js';
45
89
  /** `--scope`/`--deps`/`--dependents`/`--private`, so a plugin's command filters packages the same
46
90
  * way every built-in does rather than inventing its own flags. */
47
- export { applyPackageFilterOptions, applyRootOption, filterPackages, readPackageFilterOptions, readRootOption, } from './utils/package-filter.js';
91
+ export { applyFromRootOption, applyPackageFilterOptions, filterPackages, fromRootOption, packageFilterOptions, readFromRootOption, readPackageFilterOptions, ROOT_SELECTOR, } from './utils/package-filter.js';
48
92
  /** The live panel `run`/`build`/`clean` print - a plugin's per-package command looks like the rest
49
93
  * of rman instead of like a script someone bolted on. */
50
94
  export { formatDuration, ProgressPanel, } from './utils/progress-panel.js';
95
+ export { CiService } from './builtins/platforms/node/services/ci.service.js';
96
+ export { CleanService } from './builtins/platforms/node/services/clean.service.js';
97
+ export { PublishService } from './builtins/platforms/node/services/publish.service.js';
98
+ export { NodeVersionPlanService } from './builtins/platforms/node/services/version-plan.service.js';
99
+ export { NPM_TARGET, NpmPublishTarget } from './builtins/publish-targets/npm/npm-publish-target.js';
100
+ export { isCalendarVersion } from './utils/release-version.js';
51
101
  export { runBin } from './utils/run-bin.js';
52
102
  export { OCI_VERSION_LABEL, stampVersionConstant, stampVersionLabel } from './utils/version-stamp.js';