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 +138 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +40 -0
- package/dist/cli.js.map +1 -0
- package/dist/scan.d.ts +15 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +35 -0
- package/dist/scan.js.map +1 -0
- package/package.json +48 -0
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 @@
|
|
|
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
|
package/dist/cli.js.map
ADDED
|
@@ -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
|
package/dist/scan.js.map
ADDED
|
@@ -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
|
+
}
|