rman 1.3.0 → 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 +140 -19
  40. package/core/config.js +258 -74
  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 +137 -92
  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 -44
  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 +725 -202
  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
@@ -6,7 +6,7 @@ import semver from 'semver';
6
6
  * "is this version on the registry yet", `"workspace:"` range rewriting all assume versions that
7
7
  * can be ordered and incremented. This is the seam for an ecosystem that numbers differently (PEP
8
8
  * 440, a date-based scheme, a build counter), and it is a *meaning* seam rather than a storage one:
9
- * `ManifestProvider` answers "where is the version written", this answers "what does the next one
9
+ * `Plugin`'s manifest members answers "where is the version written", this answers "what does the next one
10
10
  * look like".
11
11
  *
12
12
  * **`bumpNames` and `bumpFor` are the interesting part.** `fix:` -> patch, `feat:` -> minor,
@@ -25,6 +25,23 @@ import semver from 'semver';
25
25
  * `SemverScheme` to keep semver's numbering and change only one of these.
26
26
  */
27
27
  export class VersionScheme {
28
+ /**
29
+ * Which prerelease line this version belongs to - `'beta'` for `2.0.0-beta.1` - or `undefined`
30
+ * when it is not a preview, or is one with no identifier to name (`2.0.0-1`).
31
+ *
32
+ * **The one thing a preview needs beyond "is it one", and it is a *name*, which is why it is
33
+ * here rather than read out of the version with a regex by whoever wants it.** npm's publish
34
+ * target derives its dist-tag from this, so a beta lands on `beta` instead of on `latest`; the
35
+ * identifier is written in the version itself, so that is a reading rather than a guess.
36
+ *
37
+ * Implemented, not abstract, and returning `undefined` by default: a scheme whose previews have
38
+ * no name (or which has no previews at all) is answering honestly, and the caller's job is to
39
+ * say so rather than invent one. `SemverScheme` overrides it.
40
+ */
41
+ prereleaseId(version) {
42
+ void version;
43
+ return undefined;
44
+ }
28
45
  /**
29
46
  * The highest of `versions` - a group's current version is the highest among its members, and a
30
47
  * monorepo root's release identity the highest among the groups.
@@ -117,6 +134,17 @@ export class SemverScheme extends VersionScheme {
117
134
  isPrerelease(version) {
118
135
  return !!semver.prerelease(version);
119
136
  }
137
+ /**
138
+ * semver's first prerelease identifier, when it is a word: `2.0.0-beta.1` -> `'beta'`.
139
+ *
140
+ * **`undefined` for a numeric-only prerelease** (`2.0.0-1`, whose identifiers are `[1]`), because
141
+ * there is no name there to use - and a caller turning that into a dist-tag called `1` would be
142
+ * inventing one. Same answer for a release, which has no prerelease at all.
143
+ */
144
+ prereleaseId(version) {
145
+ const first = semver.prerelease(version)?.[0];
146
+ return typeof first === 'string' ? first : undefined;
147
+ }
120
148
  }
121
149
  /**
122
150
  * The default, and what every package gets unless something says otherwise - so nothing about
@@ -1,3 +1,5 @@
1
+ import type { RmanApplication } from './application.js';
2
+ import type { Platform } from './plugin.js';
1
3
  /**
2
4
  * How a repository is laid out, and who decides.
3
5
  *
@@ -6,45 +8,94 @@
6
8
  * arrives as a member here rather than as another top-level export.
7
9
  */
8
10
  export declare namespace Workspace {
9
- /** What a repository looks like: where its root is, and which directories hold its packages. */
10
- interface Layout {
11
- /** Absolute path to the repository root. */
12
- root: string;
13
- /**
14
- * Absolute paths to the package **directories**, outside the root. Empty for a repository that
15
- * is itself the one package.
16
- *
17
- * Directories rather than `Package` objects, and not for want of trying: a provider runs before
18
- * any package exists - `Repository.create` constructs them from these paths afterwards, through
19
- * the *manifest* provider. A discovery provider returning packages would have to construct them
20
- * itself, making discovery the owner of identity too.
21
- *
22
- * Unlike `Package.dependencies`, there is no identity question here: these are paths, and a
23
- * path is unique by construction where a name is only unique if the ecosystem says so.
24
- */
25
- packageDirs: string[];
26
- }
27
11
  /**
28
- * How a repository's packages are found.
12
+ * **Where one directory's own child packages are** - the directories immediately below it that
13
+ * hold a package, as absolute paths. `undefined` for "this is not a directory I recognize".
14
+ *
15
+ * **A provider answers for one directory, not for the repository**, and that is the whole
16
+ * correction. It used to be `(root) => { root, packageDirs }`: asked once, at the top, by the
17
+ * first platform that recognized it - so in a polyglot repository the technology listed first in
18
+ * `plugins` decided which directories were packages *at all*. Measured, and documented as a known
19
+ * limitation for a year: a `Cargo.toml`-only package was simply not found until a provider that
20
+ * looks for both was listed first.
21
+ *
22
+ * Asked per directory, each platform only ever answers about its own packages - which is all a
23
+ * platform knows - and the recursion is the core's (see `walk`). A Cargo workspace nested inside
24
+ * a Node monorepo is then just a node in the tree whose children came from a different platform.
25
+ *
26
+ * **Children, not descendants.** A provider returning the whole subtree would have to know what
27
+ * the platforms below it consider a package; returning one level means it never has to. npm's
28
+ * `workspaces` globs are already one level by construction (`deep: 0`).
29
+ *
30
+ * Still directories rather than `Package` objects, for the reason it always was: discovery runs
31
+ * before any package exists, so a provider returning packages would make discovery the owner of
32
+ * identity too. A path is also unique by construction, where a name is only unique if the
33
+ * ecosystem says so.
34
+ */
35
+ type Provider = (dir: string) => string[] | undefined;
36
+ /**
37
+ * One directory in the walk's result: which technology claimed it, and what sits below it.
29
38
  *
30
- * **The core has no provider**, and that is the point: "packages are the `workspaces` globs in
31
- * the root `package.json`, and a package is a directory with a `package.json` in it" is true of
32
- * npm and of nothing else. `rman-node` contributes that one; a plugin for another ecosystem
33
- * contributes its own (a Cargo workspace, a `go.work`, a `pyproject.toml`).
39
+ * A plain tree rather than `Package`s, keeping the line `Provider` draws: `Repository.create`
40
+ * turns this into packages, so identity stays with the manifest provider and discovery stays
41
+ * with this one.
42
+ */
43
+ interface Node {
44
+ /** Absolute path to the directory. */
45
+ dirname: string;
46
+ /** The technology that claimed it - `basePlatform` when none did, so a reader needs no guard. */
47
+ platform: Platform;
48
+ /** The nodes for the package directories directly below it, in the order the platform gave
49
+ * them. Empty for a leaf. */
50
+ children: Node[];
51
+ }
52
+ /**
53
+ * What a directory's `.rmanrc "platform"` resolves to, when it declares one - `undefined` when
54
+ * it says nothing, so the walk falls back to the guess.
34
55
  *
35
- * Returns `undefined` for "this is not a repository I recognize", so the next provider gets a
36
- * turn and a repository nobody recognizes falls back to being a single package.
56
+ * **A callback rather than config knowledge in here**, which keeps this namespace answering one
57
+ * question. Reading a directory's cascaded config, deciding that a declared name has to be
58
+ * loaded, and refusing one that cannot be, are all the repository's business; where the packages
59
+ * are is this one's. `Repository.create` supplies it - and it is also the seam a spec uses to
60
+ * drive the declaration path without writing a config file.
37
61
  */
38
- type Provider = (root: string) => Layout | undefined;
62
+ type DeclaredPlatform = (dir: string) => Promise<Platform | undefined>;
39
63
  /**
40
- * Registers a provider. Called by `loadPlugins` for each plugin's `workspace`, in `plugins`
41
- * declaration order - so which provider answers is a function of the repository's own config.
64
+ * **The walk**: descend from `rootDir`, asking each directory's own technology where its children
65
+ * are, and repeating for each answer.
66
+ *
67
+ * One step, applied recursively:
68
+ *
69
+ * 1. take the directory's **declared** platform if it has one, and otherwise the first registered
70
+ * platform whose manifest provider recognizes it (`app.platformFor`, `basePlatform` if none);
71
+ * 2. ask **that** platform's `getWorkspace` for the directories below it holding packages;
72
+ * 3. do the same for each of them.
73
+ *
74
+ * **A declaration wins, and is then held to it.** A platform named for a directory it does not
75
+ * recognize is a statement that is simply untrue, and the failure it would otherwise become is
76
+ * invisible: the manifest reads as nothing, so the package is named after its directory at
77
+ * `0.0.0` and the repository looks like it works. The error names the directory, the platform and
78
+ * the file that platform looked for.
79
+ *
80
+ * The root node always exists - a repository is a package whatever its technology - so this never
81
+ * returns `undefined`. A repository nobody recognizes is a root with no children, which is the
82
+ * single-package answer arrived at rather than guessed.
83
+ *
84
+ * **A directory is visited once.** A provider may legitimately name a directory another one
85
+ * already claimed (two globs overlapping, a symlinked package), and a provider naming an ancestor
86
+ * would otherwise recurse forever. First visit wins, so a package sits where it was first found.
87
+ *
88
+ * `deep` bounds the descent for the same reason `findRoot` bounds its climb: a provider computing
89
+ * paths rather than reading them can produce a chain that never ends, and a guessed depth is
90
+ * better than a hang with nothing printed.
42
91
  */
43
- function addProvider(provider: Provider): void;
44
- /** For tests, which would otherwise leak a provider into every later case in the process. */
45
- function clearProviders(): void;
46
- /** The first provider that recognizes `root`, in declaration order. */
47
- function resolve(root: string): Layout | undefined;
92
+ function walk(app: RmanApplication, rootDir: string, options?: {
93
+ deep?: number;
94
+ declared?: DeclaredPlatform;
95
+ }): Promise<Node>;
96
+ /** Every node below `node`, depth-first, excluding `node` itself - the flat list `Repository`
97
+ * reports as its packages, since the root is not one of its own members. */
98
+ function flatten(node: Node): Node[];
48
99
  /**
49
100
  * Where the repository starts, decided **without knowing anything about any ecosystem** - it has
50
101
  * to be, because the plugins that do know are named in the config file this walk is looking for.
package/core/workspace.js CHANGED
@@ -10,30 +10,72 @@ import path from 'node:path';
10
10
  export var Workspace;
11
11
  (function (Workspace) {
12
12
  /**
13
- * Registers a provider. Called by `loadPlugins` for each plugin's `workspace`, in `plugins`
14
- * declaration order - so which provider answers is a function of the repository's own config.
13
+ * **The walk**: descend from `rootDir`, asking each directory's own technology where its children
14
+ * are, and repeating for each answer.
15
+ *
16
+ * One step, applied recursively:
17
+ *
18
+ * 1. take the directory's **declared** platform if it has one, and otherwise the first registered
19
+ * platform whose manifest provider recognizes it (`app.platformFor`, `basePlatform` if none);
20
+ * 2. ask **that** platform's `getWorkspace` for the directories below it holding packages;
21
+ * 3. do the same for each of them.
22
+ *
23
+ * **A declaration wins, and is then held to it.** A platform named for a directory it does not
24
+ * recognize is a statement that is simply untrue, and the failure it would otherwise become is
25
+ * invisible: the manifest reads as nothing, so the package is named after its directory at
26
+ * `0.0.0` and the repository looks like it works. The error names the directory, the platform and
27
+ * the file that platform looked for.
28
+ *
29
+ * The root node always exists - a repository is a package whatever its technology - so this never
30
+ * returns `undefined`. A repository nobody recognizes is a root with no children, which is the
31
+ * single-package answer arrived at rather than guessed.
32
+ *
33
+ * **A directory is visited once.** A provider may legitimately name a directory another one
34
+ * already claimed (two globs overlapping, a symlinked package), and a provider naming an ancestor
35
+ * would otherwise recurse forever. First visit wins, so a package sits where it was first found.
36
+ *
37
+ * `deep` bounds the descent for the same reason `findRoot` bounds its climb: a provider computing
38
+ * paths rather than reading them can produce a chain that never ends, and a guessed depth is
39
+ * better than a hang with nothing printed.
15
40
  */
16
- function addProvider(provider) {
17
- if (providers.includes(provider))
18
- return;
19
- providers.push(provider);
41
+ async function walk(app, rootDir, options) {
42
+ const visited = new Set();
43
+ const descend = async (dir, remaining) => {
44
+ const resolved = path.resolve(dir);
45
+ visited.add(resolved);
46
+ const declared = await options?.declared?.(resolved);
47
+ if (declared && !declared.manifestProvider.read(resolved)) {
48
+ throw new Error(`"${resolved}" declares \`platform: '${declared.name}'\`, and that platform does not ` +
49
+ `recognize it - it looks for "${declared.manifestProvider.fileName}". Either the ` +
50
+ `directory is not a ${declared.name} package, or the declaration belongs one level down.`);
51
+ }
52
+ const platform = declared ?? app.platformFor(resolved);
53
+ const node = { dirname: resolved, platform, children: [] };
54
+ if (remaining <= 0)
55
+ return node;
56
+ /**
57
+ * **Its own platform is asked, and nobody else.** A platform that did not claim the directory
58
+ * has no standing to say what is under it - that was the old first-wins rule, one level up.
59
+ * `basePlatform` has no `getWorkspace`, so a directory no technology claimed has no children,
60
+ * which is the documented behaviour of a repository naming no plugin.
61
+ */
62
+ for (const child of platform.getWorkspace?.(resolved) ?? []) {
63
+ const childDir = path.resolve(child);
64
+ if (visited.has(childDir))
65
+ continue;
66
+ node.children.push(await descend(childDir, remaining - 1));
67
+ }
68
+ return node;
69
+ };
70
+ return descend(rootDir, options?.deep ?? 10);
20
71
  }
21
- Workspace.addProvider = addProvider;
22
- /** For tests, which would otherwise leak a provider into every later case in the process. */
23
- function clearProviders() {
24
- providers.length = 0;
25
- }
26
- Workspace.clearProviders = clearProviders;
27
- /** The first provider that recognizes `root`, in declaration order. */
28
- function resolve(root) {
29
- for (const provider of providers) {
30
- const layout = provider(root);
31
- if (layout)
32
- return layout;
33
- }
34
- return undefined;
72
+ Workspace.walk = walk;
73
+ /** Every node below `node`, depth-first, excluding `node` itself - the flat list `Repository`
74
+ * reports as its packages, since the root is not one of its own members. */
75
+ function flatten(node) {
76
+ return node.children.flatMap(child => [child, ...flatten(child)]);
35
77
  }
36
- Workspace.resolve = resolve;
78
+ Workspace.flatten = flatten;
37
79
  /**
38
80
  * Where the repository starts, decided **without knowing anything about any ecosystem** - it has
39
81
  * to be, because the plugins that do know are named in the config file this walk is looking for.
@@ -79,5 +121,4 @@ export var Workspace;
79
121
  function hasRmanConfig(dir) {
80
122
  return CONFIG_FILES.some(name => fs.existsSync(path.join(dir, name)));
81
123
  }
82
- const providers = [];
83
124
  })(Workspace || (Workspace = {}));
package/index.d.ts CHANGED
@@ -6,44 +6,120 @@
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
  */
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 './plugins/node/augmentation/rman.augmentation.js';
33
+ import type { RmanConfig as CommandDeclaration } from './interfaces/rman-config.interface.js';
15
34
  export { defineConfig } from './core/config.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 { ConfigScope, ConfigValue, ConfigValueContext, FileScope, PackageScope, Resolved, ResolvedConfig, } from './core/config.js';
16
46
  export type { CommandContext, CustomCommand } from './core/custom-command.js';
17
47
  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';
21
48
  /** Both the shape and the registry: `const m: Manifest` and `Manifest.read(dir)` - merged onto one
22
49
  * name so a plugin can augment it the way it augments `SystemInfo`. */
50
+ export { RmanApplication } from './core/application.js';
51
+ /** The manifest seam, grouped on a plugin as `Plugin.manifestProvider`: where a package's name and
52
+ * version are written, what it declares, and how it is numbered and stamped. The core has none -
53
+ * `package.json` is npm's answer, and belongs to the `node` built-in. */
54
+ export type { ManifestProvider } from './core/manifest.js';
23
55
  export { Manifest } from './core/manifest.js';
24
56
  export { Package } from './core/package.js';
25
- export type { RmanPlugin } from './core/plugin.js';
26
- export { definePlugin } from './core/plugin.js';
57
+ /** The publish seam: where a package's artifact ships. The core brings `docker` (nobody's
58
+ * ecosystem); npm's target is the `node` built-in's, and any other technology's is its own plugin's. */
59
+ export { declaredTargets, type PublishTarget, shipsTo, targetsOf, unknownTargets } from './core/publish-target.js';
60
+ export { Registry } from './core/registry.js';
27
61
  export { Repository } from './core/repository.js';
28
62
  export type { RunConditionFn, RunStepContext, RunStepFn, RunStepValue } from './core/run-step.js';
63
+ export { Service, type ServiceFactory, type ServiceMap } from './core/service.js';
64
+ /**
65
+ * **`Platform` is one technology, whole**; **`Plugin` is whatever a package contributes**, platforms
66
+ * among them.
67
+ *
68
+ * A platform says how its packages are recognized and written, where they live, what goes on a
69
+ * child's PATH and how its releases are planned - `manifestProvider` is what makes one, and it is
70
+ * required. A plugin carries `platforms` and an `init` for anything the seams do not name yet; a
71
+ * bare `Platform` is accepted wherever a `Plugin` is, as sugar for the plugin that provides only it.
72
+ *
73
+ * **Both must be declared through their factory.** `loadPlugins` checks for the mark, because an
74
+ * rman 1.x plugin was `{ name, init }` and so is a 2.x plugin contributing nothing but an `init` -
75
+ * no shape test can tell them apart.
76
+ */
77
+ export { basePlatform, definePlatform, definePlugin, isPlatform, type Platform, type Plugin, type PluginContext, } from './core/plugin.js';
29
78
  export type { ChangeKind } from './core/version-scheme.js';
30
79
  /** The numbering seam. `VersionScheme` is abstract - `highestVersion`/`highestBump`/`smallestBump`
31
80
  * are implemented from the members around them, so a scheme states only what it must and still
32
81
  * overrides any of the three. `SemverScheme` is exported to subclass rather than restate. */
33
82
  export { assertOneScheme, SemverScheme, semverScheme, VersionScheme } from './core/version-scheme.js';
34
83
  /** 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`. */
84
+ * (see `Plugin.workspace`); the core has none, so `workspaces` is npm's idea and belongs to the
85
+ * `node` built-in. */
37
86
  /** `Workspace.Layout`, `Workspace.Provider`, `Workspace.addProvider`, `Workspace.resolve`,
38
87
  * `Workspace.findRoot` - one namespace, so a plugin can augment it. */
39
88
  export { Workspace } from './core/workspace.js';
89
+ /**
90
+ * **How a command is declared** - the same API the built-ins use, so a plugin's command is declared
91
+ * rather than built: options as data (checked for typos), positionals named against the command
92
+ * string, `--config` keys derived from what the command owns, and `ArgsOf` for the handler.
93
+ *
94
+ * `declareCommand`, not `registerCommand`: the latter pushes onto a module-level registry every
95
+ * `runCli` walks, so a plugin using it would give its commands to repositories that never named it.
96
+ * Anything else puts the function in its config's `commands`.
97
+ *
98
+ * **Flat names rather than `RmanConfig.CommandOption`**, and they outlived the reason they were
99
+ * introduced: a second file exported a `RmanConfig` too, so the namespace holding these was
100
+ * unreachable from outside the package. The two are one file now and `RmanConfig` *is* exported -
101
+ * these stay because they are the better names for the job. A plugin author declaring a flag wants
102
+ * `CommandOption`; the config it happens to contribute to is not what they are naming.
103
+ */
104
+ export { declareCommand } from './interfaces/rman-config.interface.js';
105
+ export type CommandOption = CommandDeclaration.CommandOption;
106
+ /** One declared positional, for the same reason `CommandOption` is here: it is the other half of a
107
+ * command's surface, and it was the half a plugin could not name without importing yargs. */
108
+ export type PositionalOption = CommandDeclaration.PositionalOption;
109
+ export type CommandMetadata = CommandDeclaration.CommandMetadata;
110
+ export type CommandRegisterFunction = CommandDeclaration.CommandRegisterFunction;
111
+ /** The argv a command's handler is annotated with - see `RmanConfig.ArgsOf` for why it is annotated
112
+ * rather than inferred. */
113
+ export type ArgsOf<C, Cmd extends string> = CommandDeclaration.ArgsOf<C, Cmd>;
114
+ export type GlobalArgs = CommandDeclaration.GlobalArgs;
115
+ export * from './commands.js';
40
116
  export * from './interfaces/rman-config.interface.js';
41
117
  export * from './services.js';
42
118
  /** Branch guarding: `allowBranch`/`ignoreBranch`, so a plugin's release command refuses to run on
43
119
  * the wrong branch exactly as `publish` and `version` do. */
44
- export { applyBranchGuardOptions, assertAllowedBranch, type BranchGuardOptions, readBranchGuardOptions, } from './utils/branch-guard.js';
120
+ export { applyBranchGuardOptions, assertAllowedBranch, type BranchGuardOptions, branchGuardOptions, readBranchGuardOptions, } from './utils/branch-guard.js';
45
121
  /** 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`). */
122
+ * plugin says which directories go on it (see `Plugin.binPaths`). */
47
123
  export { BinPath } from './utils/bin-path.js';
48
124
  /** A shell command, with the repository's local binaries on PATH - for a command string an author
49
125
  * wrote. `runBin` is the one to reach for when the arguments are assembled in code. */
@@ -54,13 +130,34 @@ export type { LogLevel } from './utils/logger.js';
54
130
  export { LOG_LEVELS, Logger, resolveRootLogLevel } from './utils/logger.js';
55
131
  /** `--scope`/`--deps`/`--dependents`/`--private`, so a plugin's command filters packages the same
56
132
  * 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';
133
+ export { applyFromRootOption, applyPackageFilterOptions, filterPackages, fromRootOption, type PackageFilterOptions, packageFilterOptions, readFromRootOption, readPackageFilterOptions, ROOT_SELECTOR, } from './utils/package-filter.js';
58
134
  /** The live panel `run`/`build`/`clean` print - a plugin's per-package command looks like the rest
59
135
  * of rman instead of like a script someone bolted on. */
60
136
  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
137
+ /** Version stamping helpers a `Plugin.stampVersion` can delegate to - the quoted-constant
62
138
  * pattern most languages share, and the OCI Dockerfile label (which `version` stamps itself, since
63
139
  * the label's value is by specification the package's version). */
140
+ /** A calendar version's time part (`2026.9.15-1430`) is a semver *prerelease identifier* by
141
+ * construction, so anything asking "is this a preview?" has to rule it out first - `github-release`
142
+ * does, and so must a publish target deciding whether a version needs its own dist-tag. Exported
143
+ * because that second caller lives in a plugin. */
144
+ /**
145
+ * **The `node` built-in's own surface.** It ships inside rman rather than as `rman-node`, so its
146
+ * services and target are named from here - a repository asks for the technology with
147
+ * `plugins: ['node']` (or lets detection find it) and never constructs any of this by hand.
148
+ *
149
+ * Its `.rmanrc` *keys* arrive separately, through the bare import at the top of this file - see
150
+ * there for why that import is not tidiness.
151
+ */
152
+ export { BUILTIN_PLUGINS, builtinPluginNames, isBuiltinPlugin } from './plugins/builtins.js';
153
+ export type { NodeConfigKeys, RmanNodeConfig } from './plugins/node/node-config.interface.js';
154
+ export { NPM_TARGET, NpmPublishTarget } from './plugins/node/npm-publish-target.js';
155
+ export { CiService } from './plugins/node/services/ci.service.js';
156
+ export { CleanService } from './plugins/node/services/clean.service.js';
157
+ export { PublishService } from './plugins/node/services/publish.service.js';
158
+ export { NodeVersionPlanService } from './plugins/node/services/version-plan.service.js';
159
+ export type { ParsedWorkspaceRange } from './plugins/node/utils/workspace-range.js';
160
+ export { isCalendarVersion } from './utils/release-version.js';
64
161
  export type { RunBinOptions, RunBinResult } from './utils/run-bin.js';
65
162
  export { runBin } from './utils/run-bin.js';
66
163
  export { OCI_VERSION_LABEL, stampVersionConstant, stampVersionLabel } from './utils/version-stamp.js';
package/index.js CHANGED
@@ -6,47 +6,123 @@
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
  */
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 './plugins/node/augmentation/rman.augmentation.js';
15
33
  export { defineConfig } from './core/config.js';
16
34
  export { defineCommand } from './core/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`. */
37
+ export { RmanApplication } from './core/application.js';
19
38
  export { Manifest } from './core/manifest.js';
20
39
  export { Package } from './core/package.js';
21
- export { definePlugin } from './core/plugin.js';
40
+ /** The publish seam: where a package's artifact ships. The core brings `docker` (nobody's
41
+ * ecosystem); npm's target is the `node` built-in's, and any other technology's is its own plugin's. */
42
+ export { declaredTargets, shipsTo, targetsOf, unknownTargets } from './core/publish-target.js';
43
+ export { Registry } from './core/registry.js';
22
44
  export { Repository } from './core/repository.js';
45
+ export { Service } from './core/service.js';
46
+ /**
47
+ * **`Platform` is one technology, whole**; **`Plugin` is whatever a package contributes**, platforms
48
+ * among them.
49
+ *
50
+ * A platform says how its packages are recognized and written, where they live, what goes on a
51
+ * child's PATH and how its releases are planned - `manifestProvider` is what makes one, and it is
52
+ * required. A plugin carries `platforms` and an `init` for anything the seams do not name yet; a
53
+ * bare `Platform` is accepted wherever a `Plugin` is, as sugar for the plugin that provides only it.
54
+ *
55
+ * **Both must be declared through their factory.** `loadPlugins` checks for the mark, because an
56
+ * rman 1.x plugin was `{ name, init }` and so is a 2.x plugin contributing nothing but an `init` -
57
+ * no shape test can tell them apart.
58
+ */
59
+ export { basePlatform, definePlatform, definePlugin, isPlatform, } from './core/plugin.js';
23
60
  /** The numbering seam. `VersionScheme` is abstract - `highestVersion`/`highestBump`/`smallestBump`
24
61
  * are implemented from the members around them, so a scheme states only what it must and still
25
62
  * overrides any of the three. `SemverScheme` is exported to subclass rather than restate. */
26
63
  export { assertOneScheme, SemverScheme, semverScheme, VersionScheme } from './core/version-scheme.js';
27
64
  /** 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`. */
65
+ * (see `Plugin.workspace`); the core has none, so `workspaces` is npm's idea and belongs to the
66
+ * `node` built-in. */
30
67
  /** `Workspace.Layout`, `Workspace.Provider`, `Workspace.addProvider`, `Workspace.resolve`,
31
68
  * `Workspace.findRoot` - one namespace, so a plugin can augment it. */
32
69
  export { Workspace } from './core/workspace.js';
70
+ /**
71
+ * **How a command is declared** - the same API the built-ins use, so a plugin's command is declared
72
+ * rather than built: options as data (checked for typos), positionals named against the command
73
+ * string, `--config` keys derived from what the command owns, and `ArgsOf` for the handler.
74
+ *
75
+ * `declareCommand`, not `registerCommand`: the latter pushes onto a module-level registry every
76
+ * `runCli` walks, so a plugin using it would give its commands to repositories that never named it.
77
+ * Anything else puts the function in its config's `commands`.
78
+ *
79
+ * **Flat names rather than `RmanConfig.CommandOption`**, and they outlived the reason they were
80
+ * introduced: a second file exported a `RmanConfig` too, so the namespace holding these was
81
+ * unreachable from outside the package. The two are one file now and `RmanConfig` *is* exported -
82
+ * these stay because they are the better names for the job. A plugin author declaring a flag wants
83
+ * `CommandOption`; the config it happens to contribute to is not what they are naming.
84
+ */
85
+ export { declareCommand } from './interfaces/rman-config.interface.js';
86
+ export * from './commands.js';
33
87
  export * from './interfaces/rman-config.interface.js';
34
88
  export * from './services.js';
35
89
  // --- what a command needs to behave like a built-in one -----------------------------------------
36
90
  /** Branch guarding: `allowBranch`/`ignoreBranch`, so a plugin's release command refuses to run on
37
91
  * the wrong branch exactly as `publish` and `version` do. */
38
- export { applyBranchGuardOptions, assertAllowedBranch, readBranchGuardOptions, } from './utils/branch-guard.js';
92
+ export { applyBranchGuardOptions, assertAllowedBranch, branchGuardOptions, readBranchGuardOptions, } from './utils/branch-guard.js';
39
93
  /** 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`). */
94
+ * plugin says which directories go on it (see `Plugin.binPaths`). */
41
95
  export { BinPath } from './utils/bin-path.js';
42
96
  export { exec } from './utils/exec.js';
43
97
  export { GitHelper } from './utils/git.js';
44
98
  export { LOG_LEVELS, Logger, resolveRootLogLevel } from './utils/logger.js';
45
99
  /** `--scope`/`--deps`/`--dependents`/`--private`, so a plugin's command filters packages the same
46
100
  * way every built-in does rather than inventing its own flags. */
47
- export { applyPackageFilterOptions, applyRootOption, filterPackages, readPackageFilterOptions, readRootOption, } from './utils/package-filter.js';
101
+ export { applyFromRootOption, applyPackageFilterOptions, filterPackages, fromRootOption, packageFilterOptions, readFromRootOption, readPackageFilterOptions, ROOT_SELECTOR, } from './utils/package-filter.js';
48
102
  /** The live panel `run`/`build`/`clean` print - a plugin's per-package command looks like the rest
49
103
  * of rman instead of like a script someone bolted on. */
50
104
  export { formatDuration, ProgressPanel, } from './utils/progress-panel.js';
105
+ /** Version stamping helpers a `Plugin.stampVersion` can delegate to - the quoted-constant
106
+ * pattern most languages share, and the OCI Dockerfile label (which `version` stamps itself, since
107
+ * the label's value is by specification the package's version). */
108
+ /** A calendar version's time part (`2026.9.15-1430`) is a semver *prerelease identifier* by
109
+ * construction, so anything asking "is this a preview?" has to rule it out first - `github-release`
110
+ * does, and so must a publish target deciding whether a version needs its own dist-tag. Exported
111
+ * because that second caller lives in a plugin. */
112
+ /**
113
+ * **The `node` built-in's own surface.** It ships inside rman rather than as `rman-node`, so its
114
+ * services and target are named from here - a repository asks for the technology with
115
+ * `plugins: ['node']` (or lets detection find it) and never constructs any of this by hand.
116
+ *
117
+ * Its `.rmanrc` *keys* arrive separately, through the bare import at the top of this file - see
118
+ * there for why that import is not tidiness.
119
+ */
120
+ export { BUILTIN_PLUGINS, builtinPluginNames, isBuiltinPlugin } from './plugins/builtins.js';
121
+ export { NPM_TARGET, NpmPublishTarget } from './plugins/node/npm-publish-target.js';
122
+ export { CiService } from './plugins/node/services/ci.service.js';
123
+ export { CleanService } from './plugins/node/services/clean.service.js';
124
+ export { PublishService } from './plugins/node/services/publish.service.js';
125
+ export { NodeVersionPlanService } from './plugins/node/services/version-plan.service.js';
126
+ export { isCalendarVersion } from './utils/release-version.js';
51
127
  export { runBin } from './utils/run-bin.js';
52
128
  export { OCI_VERSION_LABEL, stampVersionConstant, stampVersionLabel } from './utils/version-stamp.js';