@flowscripter/dynamic-cli-framework 9.0.2 → 9.0.4

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 (27) hide show
  1. package/dist/src/service/upgrade/DefaultUpgradeService.d.ts.map +1 -1
  2. package/dist/src/service/upgrade/DefaultUpgradeService.js +18 -330
  3. package/dist/src/service/upgrade/method/githubRelease.d.ts +6 -0
  4. package/dist/src/service/upgrade/method/githubRelease.d.ts.map +1 -0
  5. package/dist/src/service/upgrade/method/githubRelease.js +132 -0
  6. package/dist/src/service/upgrade/method/homebrew.d.ts +11 -0
  7. package/dist/src/service/upgrade/method/homebrew.d.ts.map +1 -0
  8. package/dist/src/service/upgrade/method/homebrew.js +132 -0
  9. package/dist/src/service/upgrade/method/linuxScript.d.ts +5 -0
  10. package/dist/src/service/upgrade/method/linuxScript.d.ts.map +1 -0
  11. package/dist/src/service/upgrade/method/linuxScript.js +15 -0
  12. package/dist/src/service/upgrade/method/shared.d.ts +13 -0
  13. package/dist/src/service/upgrade/method/shared.d.ts.map +1 -0
  14. package/dist/src/service/upgrade/method/shared.js +33 -0
  15. package/dist/src/service/upgrade/method/winget.d.ts +7 -0
  16. package/dist/src/service/upgrade/method/winget.d.ts.map +1 -0
  17. package/dist/src/service/upgrade/method/winget.js +49 -0
  18. package/dist/src/util/helpHelper.d.ts.map +1 -1
  19. package/dist/src/util/helpHelper.js +28 -7
  20. package/package.json +1 -1
  21. package/src/service/upgrade/DefaultUpgradeService.ts +38 -377
  22. package/src/service/upgrade/method/githubRelease.ts +163 -0
  23. package/src/service/upgrade/method/homebrew.ts +165 -0
  24. package/src/service/upgrade/method/linuxScript.ts +23 -0
  25. package/src/service/upgrade/method/shared.ts +51 -0
  26. package/src/service/upgrade/method/winget.ts +64 -0
  27. package/src/util/helpHelper.ts +31 -8
@@ -0,0 +1,132 @@
1
+ import process from "node:process";
2
+ import { existsSync, realpathSync } from "node:fs";
3
+ import { basename, join } from "node:path";
4
+ import semver, {} from "semver";
5
+ import { describeSpawnFailure, spawnQuoted } from "./shared.js";
6
+ // `brew list --versions <formula>` prints the formula name followed by one or more installed
7
+ // versions, each optionally prefixed with "v" and optionally suffixed with a "_N" revision.
8
+ export function parseBrewInstalledVersions(output, formula) {
9
+ const tokens = output.trim().split(/\s+/).filter(Boolean);
10
+ if (tokens[0] === formula) {
11
+ tokens.shift();
12
+ }
13
+ const versions = [];
14
+ for (const token of tokens) {
15
+ const version = semver.coerce(token.replace(/^v/i, "").replace(/_\d+$/, ""));
16
+ if (version) {
17
+ versions.push(version);
18
+ }
19
+ }
20
+ return versions;
21
+ }
22
+ function resolveRealExecutable() {
23
+ try {
24
+ return realpathSync(process.execPath);
25
+ }
26
+ catch {
27
+ // process.execPath may not resolve on disk (e.g. a fabricated path in tests) - fall back to
28
+ // the unresolved path rather than treating that as "not installed".
29
+ return process.execPath;
30
+ }
31
+ }
32
+ // Homebrew relinks a formula's installed binary from its Cellar directory into a `bin/` symlink,
33
+ // so resolving the running executable's real path confirms a homebrew install without spawning
34
+ // `brew`, which has a slow cold start - avoiding it keeps the background upgrade-check
35
+ // StartupTask (see UpgradeServiceProvider) fast even though it runs to completion.
36
+ export function isRunningFromHomebrewCellar(formula) {
37
+ return resolveRealExecutable().includes(`/Cellar/${formula}/`);
38
+ }
39
+ // The running executable lives in a version-specific Cellar directory, e.g.
40
+ // "<prefix>/Cellar/<formula>/<version>/bin/<exe>", while "<prefix>/opt/<formula>" links to the
41
+ // installed version, so the upgraded executable is "<prefix>/opt/<formula>/bin/<exe>".
42
+ export function resolveHomebrewOptExecutable(location) {
43
+ if (!location) {
44
+ return undefined;
45
+ }
46
+ const { formula } = location;
47
+ const realExecutable = resolveRealExecutable();
48
+ const cellarIndex = realExecutable.indexOf(`/Cellar/${formula}/`);
49
+ if (cellarIndex === -1) {
50
+ return undefined;
51
+ }
52
+ const prefix = realExecutable.slice(0, cellarIndex);
53
+ const executable = join(prefix, "opt", formula, "bin", basename(realExecutable));
54
+ return existsSync(executable) ? executable : undefined;
55
+ }
56
+ export async function isHomebrewInstalled(spawnService, location) {
57
+ if (!spawnService || !location) {
58
+ return false;
59
+ }
60
+ const result = await spawnService.spawn(["brew", "list", "--versions", location.formula], {
61
+ mode: "ignore",
62
+ longRunning: false,
63
+ });
64
+ return result.ok;
65
+ }
66
+ export async function getLatestHomebrewVersion(fetchService, location) {
67
+ if (!location) {
68
+ return { ok: false, error: new Error("No homebrew location configured") };
69
+ }
70
+ if (!fetchService) {
71
+ return { ok: false, error: new Error("FetchService is not available") };
72
+ }
73
+ const { tap, formula } = location;
74
+ const [tapOwner, tapName] = tap.split("/");
75
+ if (!tapOwner || !tapName) {
76
+ return { ok: false, error: new Error(`Invalid homebrew tap '${tap}'`) };
77
+ }
78
+ try {
79
+ const response = await fetchService.fetch(`https://raw.githubusercontent.com/${tapOwner}/homebrew-${tapName}/main/${formula}.rb`);
80
+ if (!response.ok) {
81
+ return {
82
+ ok: false,
83
+ error: new Error(`Failed to fetch homebrew formula for ${tap}/${formula}: HTTP ${response.status}`),
84
+ };
85
+ }
86
+ const text = await response.text();
87
+ const version = /version\s+"v?([^"]+)"/.exec(text)?.[1];
88
+ if (!version) {
89
+ return {
90
+ ok: false,
91
+ error: new Error(`Could not parse version from homebrew formula ${tap}/${formula}`),
92
+ };
93
+ }
94
+ return { ok: true, version };
95
+ }
96
+ catch (error) {
97
+ return {
98
+ ok: false,
99
+ error: new Error(`Failed to fetch homebrew formula for ${tap}/${formula}: ${error}`),
100
+ };
101
+ }
102
+ }
103
+ // The latest version is read from the tap's formula on GitHub, but `brew upgrade` only sees the
104
+ // local tap clone, which is stale until `brew update` runs. A stale tap makes `brew upgrade`
105
+ // exit 0 with "already installed", so update first and then confirm the installed version.
106
+ export async function upgradeViaHomebrew(spawnService, printerService, location, expectedVersion) {
107
+ const { tap, formula } = location;
108
+ const updateResult = await spawnQuoted(spawnService, printerService, ["brew", "update"]);
109
+ if (!updateResult.ok) {
110
+ throw new Error(`brew update failed: ${describeSpawnFailure(updateResult)}`);
111
+ }
112
+ const result = await spawnQuoted(spawnService, printerService, [
113
+ "brew",
114
+ "upgrade",
115
+ `${tap}/${formula}`,
116
+ ]);
117
+ if (!result.ok) {
118
+ throw new Error(`brew upgrade failed: ${describeSpawnFailure(result)}`);
119
+ }
120
+ const lines = [];
121
+ const listResult = await spawnService.spawn(["brew", "list", "--versions", formula], {
122
+ mode: "wrapped",
123
+ longRunning: false,
124
+ onOutput: (line) => lines.push(line),
125
+ });
126
+ const installed = lines.join(" ").trim();
127
+ const expected = semver.coerce(expectedVersion);
128
+ const isExpectedInstalled = parseBrewInstalledVersions(installed, formula).some((version) => expected !== null && semver.eq(version, expected));
129
+ if (!listResult.ok || !isExpectedInstalled) {
130
+ throw new Error(`brew upgrade completed but version ${expectedVersion} is not installed (installed: ${installed || "unknown"})`);
131
+ }
132
+ }
@@ -0,0 +1,5 @@
1
+ import type { PrinterService, SpawnService } from "@flowscripter/dynamic-cli-framework-api";
2
+ import type { LinuxScriptLocation } from "../UpgradeLocationsConfig.ts";
3
+ export declare function isLinuxScriptInstall(): boolean;
4
+ export declare function upgradeViaLinuxScript(spawnService: SpawnService, printerService: PrinterService | undefined, location: LinuxScriptLocation): Promise<void>;
5
+ //# sourceMappingURL=linuxScript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linuxScript.d.ts","sourceRoot":"","sources":["../../../../../src/service/upgrade/method/linuxScript.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAC5F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAGxE,wBAAgB,oBAAoB,IAAI,OAAO,CAE9C;AAED,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,YAAY,EAC1B,cAAc,EAAE,cAAc,GAAG,SAAS,EAC1C,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CASf"}
@@ -0,0 +1,15 @@
1
+ import process from "node:process";
2
+ import { describeSpawnFailure, spawnQuoted } from "./shared.js";
3
+ export function isLinuxScriptInstall() {
4
+ return process.execPath.startsWith("/usr/local/bin/");
5
+ }
6
+ export async function upgradeViaLinuxScript(spawnService, printerService, location) {
7
+ const result = await spawnQuoted(spawnService, printerService, [
8
+ "sh",
9
+ "-c",
10
+ `curl -fsSL ${location.scriptUrl} | sh`,
11
+ ]);
12
+ if (!result.ok) {
13
+ throw new Error(`Install script failed: ${describeSpawnFailure(result)}`);
14
+ }
15
+ }
@@ -0,0 +1,13 @@
1
+ import type { PrinterService, SpawnResult, SpawnService } from "@flowscripter/dynamic-cli-framework-api";
2
+ export type VersionLookupResult = {
3
+ readonly ok: true;
4
+ readonly version: string;
5
+ } | {
6
+ readonly ok: false;
7
+ readonly error: Error;
8
+ };
9
+ export declare function describeSpawnFailure(result: Extract<SpawnResult, {
10
+ ok: false;
11
+ }>): string;
12
+ export declare function spawnQuoted(spawnService: SpawnService, printerService: PrinterService | undefined, command: ReadonlyArray<string>): Promise<SpawnResult>;
13
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../../src/service/upgrade/method/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,YAAY,EACb,MAAM,yCAAyC,CAAC;AAEjD,MAAM,MAAM,mBAAmB,GAC3B;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAElD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,EAAE;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,GAAG,MAAM,CAIxF;AAKD,wBAAsB,WAAW,CAC/B,YAAY,EAAE,YAAY,EAC1B,cAAc,EAAE,cAAc,GAAG,SAAS,EAC1C,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAC7B,OAAO,CAAC,WAAW,CAAC,CA2BtB"}
@@ -0,0 +1,33 @@
1
+ export function describeSpawnFailure(result) {
2
+ return "timedOut" in result
3
+ ? "timed out"
4
+ : (result.error?.message ?? `exit code ${result.exitCode}`);
5
+ }
6
+ // Mirrors SpawnInterfaceAdapter's plugin:add/plugin:remove pattern: wrap a spawned command's
7
+ // output in a quoted, marked block that's cleared on success (so a clean install stays quiet)
8
+ // but left on screen on failure (so the diagnostic output remains visible).
9
+ export async function spawnQuoted(spawnService, printerService, command) {
10
+ if (!printerService) {
11
+ return spawnService.spawn(command, { mode: "ignore" });
12
+ }
13
+ printerService.startQuote();
14
+ printerService.startMark();
15
+ // onOutput is synchronous and may be called concurrently for stdout/stderr lines, but
16
+ // printerService.info() is async and must not be invoked concurrently with itself - queue
17
+ // writes so they're applied one at a time, in call order.
18
+ let writeQueue = Promise.resolve();
19
+ const onOutput = (line) => {
20
+ writeQueue = writeQueue.then(() => printerService.info(`${line}\n`));
21
+ };
22
+ const result = await spawnService.spawn(command, { mode: "wrapped", onOutput });
23
+ await writeQueue;
24
+ printerService.endQuote();
25
+ printerService.endMark();
26
+ if (result.ok) {
27
+ await printerService.clearMarked();
28
+ }
29
+ else {
30
+ printerService.discardMark();
31
+ }
32
+ return result;
33
+ }
@@ -0,0 +1,7 @@
1
+ import type { PrinterService, SpawnService } from "@flowscripter/dynamic-cli-framework-api";
2
+ import type { WingetLocation } from "../UpgradeLocationsConfig.ts";
3
+ import { type VersionLookupResult } from "./shared.ts";
4
+ export declare function isWingetInstalled(spawnService: SpawnService | undefined, location: WingetLocation | undefined): Promise<boolean>;
5
+ export declare function getLatestWingetVersion(spawnService: SpawnService | undefined, location: WingetLocation | undefined): Promise<VersionLookupResult>;
6
+ export declare function upgradeViaWinget(spawnService: SpawnService, printerService: PrinterService | undefined, location: WingetLocation): Promise<void>;
7
+ //# sourceMappingURL=winget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"winget.d.ts","sourceRoot":"","sources":["../../../../../src/service/upgrade/method/winget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAC5F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAqC,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAE1F,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,YAAY,GAAG,SAAS,EACtC,QAAQ,EAAE,cAAc,GAAG,SAAS,GACnC,OAAO,CAAC,OAAO,CAAC,CASlB;AAED,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,YAAY,GAAG,SAAS,EACtC,QAAQ,EAAE,cAAc,GAAG,SAAS,GACnC,OAAO,CAAC,mBAAmB,CAAC,CAuB9B;AAED,wBAAsB,gBAAgB,CACpC,YAAY,EAAE,YAAY,EAC1B,cAAc,EAAE,cAAc,GAAG,SAAS,EAC1C,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC,CAaf"}
@@ -0,0 +1,49 @@
1
+ import { describeSpawnFailure, spawnQuoted } from "./shared.js";
2
+ export async function isWingetInstalled(spawnService, location) {
3
+ if (!spawnService || !location) {
4
+ return false;
5
+ }
6
+ const result = await spawnService.spawn(["winget", "list", "--id", location.packageId], {
7
+ mode: "ignore",
8
+ longRunning: false,
9
+ });
10
+ return result.ok;
11
+ }
12
+ export async function getLatestWingetVersion(spawnService, location) {
13
+ if (!location) {
14
+ return { ok: false, error: new Error("No winget location configured") };
15
+ }
16
+ if (!spawnService) {
17
+ return { ok: false, error: new Error("SpawnService is not available") };
18
+ }
19
+ const lines = [];
20
+ const result = await spawnService.spawn(["winget", "show", "--id", location.packageId], {
21
+ mode: "wrapped",
22
+ longRunning: false,
23
+ onOutput: (line) => lines.push(line),
24
+ });
25
+ if (!result.ok) {
26
+ return { ok: false, error: new Error(`winget show failed: ${describeSpawnFailure(result)}`) };
27
+ }
28
+ for (const line of lines) {
29
+ const match = /Version:\s*(\S+)/.exec(line);
30
+ if (match?.[1]) {
31
+ return { ok: true, version: match[1] };
32
+ }
33
+ }
34
+ return { ok: false, error: new Error("Could not parse version from winget output") };
35
+ }
36
+ export async function upgradeViaWinget(spawnService, printerService, location) {
37
+ const result = await spawnQuoted(spawnService, printerService, [
38
+ "winget",
39
+ "upgrade",
40
+ "--id",
41
+ location.packageId,
42
+ "--silent",
43
+ "--accept-package-agreements",
44
+ "--accept-source-agreements",
45
+ ]);
46
+ if (!result.ok) {
47
+ throw new Error(`winget upgrade failed: ${describeSpawnFailure(result)}`);
48
+ }
49
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"helpHelper.d.ts","sourceRoot":"","sources":["../../../src/util/helpHelper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAG7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAIvE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAM5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAG9E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AAMrF,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CACxC;AAsDD,wBAAsB,iBAAiB,CACrC,cAAc,EAAE,cAAc,EAC9B,qBAAqB,EAAE,qBAAqB,EAC5C,QAAQ,EAAE,KAAK,CAAC,WAAW,CAAC,GAC3B,OAAO,CAAC,IAAI,CAAC,CA2Cf;AA2BD,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,OAAO,EACvB,aAAa,EAAE,aAAa,GAC3B,SAAS,CA6DX;AAED,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CA+C3E;AAkLD,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,OAAO,EACvB,UAAU,EAAE,UAAU,EACtB,kBAAkB,EAAE,OAAO,GAC1B,KAAK,CAAC,WAAW,CAAC,CAoBpB;AACD,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,UAAU,GACrB,KAAK,CAAC,WAAW,CAAC,CAgCpB;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,EAChB,sBAAsB,EAAE,aAAa,CAAC,qBAAqB,CAAC,EAC5D,cAAc,EAAE,aAAa,CAAC,aAAa,CAAC,EAC5C,aAAa,EAAE,aAAa,CAAC,YAAY,CAAC,EAC1C,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,GACrC,MAAM,CA8IR;AAED,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,aAAa,CAAC,YAAY,CAAC,EAC1C,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,GACrC,MAAM,EAAE,CAkBV"}
1
+ {"version":3,"file":"helpHelper.d.ts","sourceRoot":"","sources":["../../../src/util/helpHelper.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAG7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAIvE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAM5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAG9E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AA6BrF,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CACxC;AAsDD,wBAAsB,iBAAiB,CACrC,cAAc,EAAE,cAAc,EAC9B,qBAAqB,EAAE,qBAAqB,EAC5C,QAAQ,EAAE,KAAK,CAAC,WAAW,CAAC,GAC3B,OAAO,CAAC,IAAI,CAAC,CA2Cf;AA2BD,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,OAAO,EACvB,aAAa,EAAE,aAAa,GAC3B,SAAS,CA6DX;AAED,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CA+C3E;AAkLD,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,OAAO,EACvB,UAAU,EAAE,UAAU,EACtB,kBAAkB,EAAE,OAAO,GAC1B,KAAK,CAAC,WAAW,CAAC,CAoBpB;AACD,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,UAAU,GACrB,KAAK,CAAC,WAAW,CAAC,CAgCpB;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,EAChB,sBAAsB,EAAE,aAAa,CAAC,qBAAqB,CAAC,EAC5D,cAAc,EAAE,aAAa,CAAC,aAAa,CAAC,EAC5C,aAAa,EAAE,aAAa,CAAC,YAAY,CAAC,EAC1C,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,GACrC,MAAM,CA8IR;AAED,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,aAAa,CAAC,YAAY,CAAC,EAC1C,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,GACrC,MAAM,EAAE,CAkBV"}
@@ -4,6 +4,27 @@ import { getGlobalCommandArgumentConfigurationKey, getSubCommandArgumentConfigur
4
4
  const SYNTAX_INDENT_WIDTH = 2;
5
5
  const MINIMUM_SYNTAX_COLUMN_WIDTH = 15;
6
6
  const HELP_SYNTAX_INDENT = " ".repeat(SYNTAX_INDENT_WIDTH);
7
+ const USER_INPUT_START = "\uE000";
8
+ const USER_INPUT_END = "\uE001";
9
+ const USER_INPUT_PATTERN = new RegExp(`${USER_INPUT_START}(.*?)${USER_INPUT_END}`, "gs");
10
+ function userInput(value) {
11
+ return `${USER_INPUT_START}${value}${USER_INPUT_END}`;
12
+ }
13
+ function renderDescription(printerService, description) {
14
+ let rendered = "";
15
+ let last = 0;
16
+ for (const match of description.matchAll(USER_INPUT_PATTERN)) {
17
+ if (match.index > last) {
18
+ rendered += printerService.secondary(description.slice(last, match.index));
19
+ }
20
+ rendered += printerService.primary(match[1]);
21
+ last = match.index + match[0].length;
22
+ }
23
+ if (last < description.length) {
24
+ rendered += printerService.secondary(description.slice(last));
25
+ }
26
+ return rendered;
27
+ }
7
28
  function flattenHelpEntry(printerService, helpEntry, indentText, isRoot, isLastSibling, rows) {
8
29
  let syntax = "";
9
30
  if (helpEntry.syntax) {
@@ -22,7 +43,7 @@ function flattenHelpEntry(printerService, helpEntry, indentText, isRoot, isLastS
22
43
  }
23
44
  }
24
45
  const description = helpEntry.description
25
- ? printerService.secondary(helpEntry.description)
46
+ ? renderDescription(printerService, helpEntry.description)
26
47
  : undefined;
27
48
  rows.push({ syntax, description });
28
49
  if (helpEntry.helpSubEntries !== undefined) {
@@ -101,7 +122,7 @@ export function getGlobalArgumentHelpEntry(cliConfig, includeEnvVars, globalComm
101
122
  const syntax = getGlobalCommandArgumentForm(argument);
102
123
  const notesItems = [];
103
124
  if (argument.allowableValues !== undefined && argument.allowableValues.length > 0) {
104
- notesItems.push(`valid values: ${argument.allowableValues.join("|")}`);
125
+ notesItems.push(`valid values: ${userInput(argument.allowableValues.join("|"))}`);
105
126
  }
106
127
  else {
107
128
  switch (argument.type) {
@@ -127,7 +148,7 @@ export function getGlobalArgumentHelpEntry(cliConfig, includeEnvVars, globalComm
127
148
  }
128
149
  }
129
150
  if (argument.defaultValue !== undefined) {
130
- notesItems.push(`default: ${argument.defaultValue}`);
151
+ notesItems.push(`default: ${userInput(String(argument.defaultValue))}`);
131
152
  }
132
153
  if (includeEnvVars) {
133
154
  const configurationKey = getGlobalCommandArgumentConfigurationKey(cliConfig, globalCommand, argument);
@@ -214,7 +235,7 @@ function getOptionHelpEntry(cliConfig, includeEnvVars, command, option, subEntry
214
235
  }
215
236
  else {
216
237
  if (option.allowableValues !== undefined && option.allowableValues.length > 0) {
217
- notesItems.push(`valid values: ${option.allowableValues.join("|")}`);
238
+ notesItems.push(`valid values: ${userInput(option.allowableValues.join("|"))}`);
218
239
  }
219
240
  else {
220
241
  switch (option.type) {
@@ -244,9 +265,9 @@ function getOptionHelpEntry(cliConfig, includeEnvVars, command, option, subEntry
244
265
  notesItems.push(range);
245
266
  }
246
267
  if (option.defaultValue !== undefined) {
247
- notesItems.push(`default: ${Array.isArray(option.defaultValue)
268
+ notesItems.push(`default: ${userInput(Array.isArray(option.defaultValue)
248
269
  ? `${option.defaultValue.join(", ")}`
249
- : `${option.defaultValue}`}`);
270
+ : `${option.defaultValue}`)}`);
250
271
  }
251
272
  if (option.isOptional) {
252
273
  notesItems.push("optional");
@@ -282,7 +303,7 @@ function getOptionHelpEntry(cliConfig, includeEnvVars, command, option, subEntry
282
303
  function getPositionalHelpEntry(cliConfig, includeEnvVars, command, positional) {
283
304
  const notesItems = [];
284
305
  if (positional.allowableValues !== undefined && positional.allowableValues.length > 0) {
285
- notesItems.push(`valid values: ${positional.allowableValues.join("|")}`);
306
+ notesItems.push(`valid values: ${userInput(positional.allowableValues.join("|"))}`);
286
307
  }
287
308
  else {
288
309
  switch (positional.type) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowscripter/dynamic-cli-framework",
3
- "version": "9.0.2",
3
+ "version": "9.0.4",
4
4
  "description": "A framework for developing CLI applications which supports dynamic discovery and installation of new commands",
5
5
  "keywords": [
6
6
  "bun",