mx-empty-captions 1.0.0

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.
package/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # mx-empty-captions
2
+
3
+ CLI tool that scans a Mendix app for **Exclusive Split** (decision) activities with empty captions, then writes the results to a JSON file.
4
+
5
+ ## Prerequisites
6
+
7
+ - [Node.js](https://nodejs.org/) v18 or later
8
+ - A Mendix **Personal Access Token (PAT)**
9
+ - Your Mendix **App ID** (Project ID)
10
+
11
+ ## Quick start
12
+
13
+ ### Run without installing
14
+
15
+ ```bash
16
+ npx mx-empty-captions --help
17
+ ```
18
+
19
+ ### Install globally
20
+
21
+ ```bash
22
+ npm install -g mx-empty-captions
23
+ ```
24
+
25
+ ### Set your PAT
26
+
27
+ The Mendix Platform SDK reads your token from the `MENDIX_TOKEN` environment variable.
28
+
29
+ **Linux / macOS:**
30
+
31
+ ```bash
32
+ export MENDIX_TOKEN="your-pat-here"
33
+ ```
34
+
35
+ **Windows (PowerShell):**
36
+
37
+ ```powershell
38
+ $env:MENDIX_TOKEN = "your-pat-here"
39
+ ```
40
+
41
+ **Windows (cmd):**
42
+
43
+ ```cmd
44
+ set MENDIX_TOKEN=your-pat-here
45
+ ```
46
+
47
+ Alternatively, pass `--token` on the command line (less secure in shared environments).
48
+
49
+ ### Create a PAT
50
+
51
+ 1. Open the [Mendix Developer Portal](https://sprintr.home.mendix.com/).
52
+ 2. Go to your profile → **Personal Access Tokens**.
53
+ 3. Create a token with access to the app you want to scan.
54
+
55
+ Official guide: [Set up your PAT](https://docs.mendix.com/apidocs-mxsdk/mxsdk/setup-your-pat/)
56
+
57
+ > **Warning:** Never commit a real PAT to Git or share it in scripts.
58
+
59
+ ### Find your App ID
60
+
61
+ 1. Open [Mendix Developer Portal](https://sprintr.home.mendix.com/).
62
+ 2. Open the app you want to scan.
63
+ 3. Copy the UUID from the app URL:
64
+
65
+ ```
66
+ https://sprintr.home.mendix.com/link/myapps/<APP-ID-HERE>
67
+ ```
68
+
69
+ Example: `81977b9a-2dfa-4f2d-b179-b72ea4b00e20`
70
+
71
+ ## Usage
72
+
73
+ ```bash
74
+ mx-empty-captions --app-id 81977b9a-2dfa-4f2d-b179-b72ea4b00e20
75
+ ```
76
+
77
+ ```bash
78
+ mx-empty-captions -a 81977b9a-2dfa-4f2d-b179-b72ea4b00e20 -b trunk -o results.json
79
+ ```
80
+
81
+ ### Options
82
+
83
+ | Option | Short | Required | Default | Description |
84
+ | ----------- | ----- | -------- | --------------------------- | --------------------------------------- |
85
+ | `--app-id` | `-a` | Yes | — | Mendix app ID (Project ID) |
86
+ | `--branch` | `-b` | No | `main` | Team Server branch for the working copy |
87
+ | `--output` | `-o` | No | `found_Empty_Captions.json` | Output JSON file path |
88
+ | `--token` | `-t` | No | — | PAT (overrides `MENDIX_TOKEN`) |
89
+ | `--help` | `-h` | No | — | Show help |
90
+ | `--version` | `-V` | No | — | Show version |
91
+
92
+ ## Output
93
+
94
+ After a successful run, the tool creates a JSON file (default: `found_Empty_Captions.json` in the current directory).
95
+
96
+ Example:
97
+
98
+ ```json
99
+ [
100
+ {
101
+ "module": "MyFirstModule",
102
+ "microflows": [
103
+ {
104
+ "microflow": "MyFirstLogic",
105
+ "emptyCaptions": 1
106
+ }
107
+ ]
108
+ }
109
+ ]
110
+ ```
111
+
112
+ Each module appears once. Under `microflows`, each entry lists a microflow and how many decision activities have an empty caption.
113
+
114
+ ## Troubleshooting
115
+
116
+ | Error | Likely cause |
117
+ | ------------------------------- | ------------------------------------------------------------------------------------------- |
118
+ | `No Mendix PAT found` | Set `MENDIX_TOKEN` or pass `--token` |
119
+ | `RS400` / branch does not exist | Wrong branch — try `trunk` instead of `main` with `-b trunk` |
120
+ | Authentication failed | PAT expired, revoked, or missing permissions for the app |
121
+ | App not found | Request failed, POST '/v1/temporary-working-copies/' message: Response code 403 (Forbidden) |
122
+
123
+ ## Development
124
+
125
+ Clone the repo and install dependencies:
126
+
127
+ ```bash
128
+ git clone <repo-url>
129
+ cd MX_SDK_GetEmptyCaptionsOnDecisions
130
+ npm install
131
+ npm run build
132
+ npm link
133
+ mx-empty-captions --help
134
+ ```
135
+
136
+ ## License
137
+
138
+ ISC
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import * as mendixPlatformSDK from "mendixplatformsdk";
4
+ import { scanEmptyCaptions } from "./scan.js";
5
+ function resolveToken(cliToken) {
6
+ return cliToken ?? process.env.MENDIX_TOKEN;
7
+ }
8
+ const program = new Command();
9
+ program
10
+ .name("mx-empty-captions")
11
+ .description("Scan a Mendix app for decision activities (Exclusive Split) with empty captions")
12
+ .version("1.0.0")
13
+ .requiredOption("-a, --app-id <uuid>", "Mendix app ID (Project ID)")
14
+ .option("-b, --branch <name>", "Team Server branch", "main")
15
+ .option("-o, --output <path>", "Output JSON file path", "found_Empty_Captions.json")
16
+ .option("-t, --token <pat>", "Mendix Personal Access Token (defaults to MENDIX_TOKEN env var)")
17
+ .action(async (options) => {
18
+ const token = resolveToken(options.token);
19
+ if (!token) {
20
+ console.error("Error: No Mendix PAT found. Set MENDIX_TOKEN or pass --token.");
21
+ process.exit(1);
22
+ }
23
+ if (options.token) {
24
+ mendixPlatformSDK.setPlatformConfig({ mendixToken: token });
25
+ }
26
+ try {
27
+ const results = await scanEmptyCaptions({
28
+ appId: options.appId,
29
+ branch: options.branch,
30
+ outputPath: options.output,
31
+ });
32
+ console.log(`Wrote ${results.length} module(s) to ${options.output}`);
33
+ }
34
+ catch (error) {
35
+ console.error("Error:", error instanceof Error ? error.message : error);
36
+ process.exit(1);
37
+ }
38
+ });
39
+ program.parse();
40
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,iBAAiB,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,SAAS,YAAY,CAAC,QAA4B;IAChD,OAAO,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;AAC9C,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,mBAAmB,CAAC;KACzB,WAAW,CACV,iFAAiF,CAClF;KACA,OAAO,CAAC,OAAO,CAAC;KAChB,cAAc,CAAC,qBAAqB,EAAE,4BAA4B,CAAC;KACnE,MAAM,CAAC,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,CAAC;KAC3D,MAAM,CACL,qBAAqB,EACrB,uBAAuB,EACvB,2BAA2B,CAC5B;KACA,MAAM,CACL,mBAAmB,EACnB,iEAAiE,CAClE;KACA,MAAM,CAAC,KAAK,EAAE,OAA0E,EAAE,EAAE;IAC3F,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAE1C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CACX,+DAA+D,CAChE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,iBAAiB,CAAC,iBAAiB,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC;YACtC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,UAAU,EAAE,OAAO,CAAC,MAAM;SAC3B,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CACT,SAAS,OAAO,CAAC,MAAM,iBAAiB,OAAO,CAAC,MAAM,EAAE,CACzD,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
package/dist/scan.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ export type MicroflowResult = {
2
+ microflow: string;
3
+ emptyCaptions: number;
4
+ };
5
+ export type ModuleResult = {
6
+ module: string;
7
+ microflows: MicroflowResult[];
8
+ };
9
+ export type ScanOptions = {
10
+ appId: string;
11
+ branch: string;
12
+ outputPath: string;
13
+ };
14
+ export declare function scanEmptyCaptions(options: ScanOptions): Promise<ModuleResult[]>;
15
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,eAAe,EAAE,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,EAAE,CAAC,CAyCzB"}
package/dist/scan.js ADDED
@@ -0,0 +1,35 @@
1
+ import { MendixPlatformClient } from "mendixplatformsdk";
2
+ import { microflows } from "mendixmodelsdk";
3
+ import { writeFile } from "node:fs/promises";
4
+ import { resolve } from "node:path";
5
+ export async function scanEmptyCaptions(options) {
6
+ const client = new MendixPlatformClient();
7
+ const app = await client.getApp(options.appId);
8
+ const workingCopy = await app.createTemporaryWorkingCopy(options.branch);
9
+ const model = await workingCopy.openModel();
10
+ const allMicroflows = model.allMicroflows();
11
+ const modulesByName = new Map();
12
+ for (const mc of allMicroflows) {
13
+ const loadedMc = await mc.load();
14
+ const emptyExclusiveSplits = loadedMc.objectCollection.objects.filter((activity) => activity instanceof microflows.ExclusiveSplit &&
15
+ activity.caption === "");
16
+ if (emptyExclusiveSplits.length === 0) {
17
+ continue;
18
+ }
19
+ const moduleName = loadedMc.containerAsModule.name;
20
+ let moduleEntry = modulesByName.get(moduleName);
21
+ if (!moduleEntry) {
22
+ moduleEntry = { module: moduleName, microflows: [] };
23
+ modulesByName.set(moduleName, moduleEntry);
24
+ }
25
+ moduleEntry.microflows.push({
26
+ microflow: loadedMc.name,
27
+ emptyCaptions: emptyExclusiveSplits.length,
28
+ });
29
+ }
30
+ const results = [...modulesByName.values()];
31
+ const outputPath = resolve(process.cwd(), options.outputPath);
32
+ await writeFile(outputPath, JSON.stringify(results, null, 2), "utf-8");
33
+ return results;
34
+ }
35
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../src/scan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkBpC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAoB;IAEpB,MAAM,MAAM,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC1C,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,MAAM,GAAG,CAAC,0BAA0B,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACzE,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,SAAS,EAAE,CAAC;IAE5C,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,EAAE,CAAC;IAC5C,MAAM,aAAa,GAAG,IAAI,GAAG,EAAwB,CAAC;IAEtD,KAAK,MAAM,EAAE,IAAI,aAAa,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;QACjC,MAAM,oBAAoB,GAAG,QAAQ,CAAC,gBAAgB,CAAC,OAAO,CAAC,MAAM,CACnE,CAAC,QAAoC,EAAE,EAAE,CACvC,QAAQ,YAAY,UAAU,CAAC,cAAc;YAC7C,QAAQ,CAAC,OAAO,KAAK,EAAE,CAC1B,CAAC;QAEF,IAAI,oBAAoB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,SAAS;QACX,CAAC;QAED,MAAM,UAAU,GAAG,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC;QACnD,IAAI,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAEhD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,WAAW,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;YACrD,aAAa,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAC7C,CAAC;QAED,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC;YAC1B,SAAS,EAAE,QAAQ,CAAC,IAAI;YACxB,aAAa,EAAE,oBAAoB,CAAC,MAAM;SAC3C,CAAC,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAE9D,MAAM,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAEvE,OAAO,OAAO,CAAC;AACjB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "mx-empty-captions",
3
+ "version": "1.0.0",
4
+ "description": "Scan Mendix apps for decision activities with empty captions",
5
+ "type": "module",
6
+ "main": "dist/cli.js",
7
+ "bin": {
8
+ "mx-empty-captions": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "watch": "tsc --watch",
19
+ "start": "node dist/cli.js",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "mendix",
24
+ "mxsdk",
25
+ "microflow",
26
+ "cli"
27
+ ],
28
+ "author": "hasan <hasan-net2010@hotmail.com>",
29
+ "license": "ISC",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/Hasan-droid/MX_SDK_EmptyCaptions.git"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/Hasan-droid/MX_SDK_EmptyCaptions/issues"
36
+ },
37
+ "homepage": "https://github.com/Hasan-droid/MX_SDK_EmptyCaptions#readme",
38
+ "devDependencies": {
39
+ "@types/node": "^26.1.1",
40
+ "tsx": "^4.23.1",
41
+ "typescript": "^7.0.2"
42
+ },
43
+ "dependencies": {
44
+ "commander": "^14.0.0",
45
+ "mendixmodelsdk": "^4.114.0",
46
+ "mendixplatformsdk": "^5.2.0"
47
+ }
48
+ }