@ubercode/mirth-connect-types
v0.3.0
Published
TypeScript type definitions for the Mirth Connect / NextGen Connect server-side JavaScript (Rhino) User API.
Maintainers
Readme
@ubercode/mirth-connect-types
TypeScript type definitions for the Mirth Connect / NextGen Connect server-side
JavaScript (Rhino) User API — the globals, $-map accessors, and Java/com.mirth.connect
classes available inside channel scripts, transformers, and code templates.
[!IMPORTANT] Preview: types cover NextGen Connect 4.5.2 only. They're checked against a production channel codebase and against Mirth's own Rhino, but gaps remain. Please report them. More versions and forks (Open Integration Engine, BridgeLink) are planned; see Project status & roadmap.
Why
Mirth channel code runs on Rhino with no module system and a large, mostly-undocumented
surface of Java and com.mirth.connect.* classes. These definitions give you editor
IntelliSense, inline Javadoc, and compile-time checking for that surface.
Install
npm i -D @ubercode/mirth-connect-types # or: pnpm add -D … / yarn add -D …Usage
Mirth Connect runs your channel scripts as JavaScript on Rhino — it does not support
TypeScript. You keep writing JavaScript and paste that same .js into Mirth as always; this
package simply gives your editor full type information for it: autocomplete, inline Javadoc,
parameter hints, and — if you opt in — type-checking. Nothing is compiled or bundled for
Mirth. The types live only in your editor/checker, never in what you deploy.
The definitions are ambient globals (no import — mirroring how Mirth scripts run). The
User API utility classes are exposed as unqualified globals — ChannelUtil, AttachmentUtil,
DateUtil, FileUtil, HTTPUtil, SerializerFactory, Lists/Maps, … — and the Status
constants (SENT, QUEUED, ERROR, …) too, exactly as Mirth injects them. Java methods accept
the JS values Rhino converts for them: strings, numbers, booleans, arrays for List/Collection,
and objects for Map. Return values keep their Java type, so wrap a returned java.lang.String
with String(...) before passing it to JS APIs such as JSON.parse.
// transformer.js — plain JavaScript, exactly what you paste into Mirth
$c('patientId', msg['PID']['PID.3']['PID.3.1'].toString());
const name = ChannelUtil.getChannelName(channelId); // ← autocomplete, hover docs, checkingmsg and tmp are typed any, because their shape depends on the channel's data type (E4X XML,
parsed JSON, or text). For E4X completions in an HL7 or XML script, cast once:
var hl7 = /** @type {XML} */ (msg);.
➡️ Editor setup wires these into VS Code or WebStorm in about a minute — no TypeScript project required.
Editor setup
These definitions work in plain JavaScript projects — you do not need to adopt TypeScript. The package ships ambient declarations; you just point your editor at them.
Use TypeScript 6.x for checking. The declarations compile on TypeScript 5.9 through 7, but
TypeScript 7 stopped inferring ES5 constructor functions (@constructor included) and treats
Closure-style JSDoc such as {function(string): number} as a parse error, so it can't check
typical Mirth code. Pin typescript@6 in the project and point your editor at the workspace
version.
1. Add the package to the project that holds your Mirth scripts:
pnpm add -D @ubercode/mirth-connect-types # or: npm i -D … / yarn add -D …2. Activate the types with one small declaration file at the project root — call it anything,
e.g. mirth.d.ts:
/// <reference types="@ubercode/mirth-connect-types" />That one line makes every Mirth global (msg, tmp, $c, ChannelUtil, Status, …) available
to all the .js files in the project. The bare specifier loads the default version; to pin one
explicitly, use the subpath form
/// <reference types="@ubercode/mirth-connect-types/nextgen-connect/v4.5.2" /> together with
"moduleResolution": "bundler" (see TypeScript projects below).
VS Code (JavaScript)
Add a jsconfig.json at the project root so VS Code treats the folder as a JS project:
{
"compilerOptions": {
"target": "ES2017",
// What Mirth 4.5.2's Rhino provides; see "Rhino language support". Do not add "dom".
"lib": ["ES2015", "ES2016.Array.Include", "ES2017.String", "ES2019.String"],
"checkJs": false, // set true to type-check every .js, or use `// @ts-check` per file
},
"include": ["**/*.js", "mirth.d.ts"],
}- Autocomplete and hover docs work immediately.
- Want mistakes flagged as errors? Set
"checkJs": true, or add// @ts-checkto the top of individual scripts for opt-in, file-by-file checking.
WebStorm / IntelliJ IDEA (JavaScript)
WebStorm automatically uses type definitions found in node_modules and honors jsconfig.json,
so the mirth.d.ts + jsconfig.json above is enough — globals resolve in code completion and
quick documentation out of the box. If completion doesn't appear:
- confirm the project's
node_modulesis recognized (Settings → Languages & Frameworks → Node.js), or - register it explicitly: Settings → Languages & Frameworks → JavaScript → Libraries → Add…
and point at
node_modules/@ubercode/mirth-connect-types.
For inline error highlighting, enable the TypeScript service (it checks .js too) under
Settings → Languages & Frameworks → TypeScript, or add // @ts-check per file.
TypeScript projects (optional)
If you author tooling in a TypeScript-aware project, you can skip mirth.d.ts and reference the
version directly in tsconfig.json:
{
"compilerOptions": {
"moduleResolution": "bundler",
"types": ["@ubercode/mirth-connect-types/nextgen-connect/v4.5.2"],
},
}Either way, the code that runs in Mirth is still JavaScript — the types are purely an editor/checker aid, not a build step.
Verified end-to-end: every
checkpacks the tarball, installs it in a fresh project, type-checks a Mirth script (subpathexports, ambient globals,skipLibCheck: falsewith the DOMlib), and runs the installedmirth-types-report(pnpm run test:consumer).
Rhino language support
Mirth 4.5.2 runs Rhino 1.7.13. What scripts can use depends on rhino.languageversion in
mirth.properties: a fresh 4.5.2 install sets es6, but a server upgraded from an older Mirth
may still be on 1.8 or lower. Check before relying on ES6 features. Under es6 you get ES5
plus part of ES2015, and TypeScript can't check the gaps, so they fail only in Mirth:
constinside a loop keeps its first value. Rhino scopes it to the function and ignores later initializations, sofor (…) { const c = i * 10; out.push(c); }pushes the same value every time. Assigning to aconstis silently ignored too. Inside loop bodies uselet, which is re-created each iteration.for (let i …)shares onei. Closures created in the loop all see the final value.- A JS number passed where Java expects
Objectbecomes aDouble. Somap.get(1)andlist.contains(1)silently missIntegerkeys and elements. Usejava.lang.Integer.valueOf(1). The types enforce this: a lookup on anInteger-keyed map rejects a JS number.message.getConnectorMessages().get(1)is the exception, because Mirth converts the key there. - Template literals don't interpolate.
`id ${n}`evaluates to the literal textid ${n}. Use string concatenation. - Not supported: spread (
f(...args)),class, and default parameters (function (a = 1)). - Supported:
let, arrow functions, destructuring,Array.prototype.includes,padStart/padEnd, andtrimStart.for…of,Map, andSetexist only withes6: at1.8,for…ofis a syntax error andMap/Setare undefined. - Missing built-ins:
Object.values/entries/fromEntries,Array.prototype.flat/flatMap, andPromise. Theliblist above leaves out all of these exceptPromise, which comes withES2015.
Troubleshooting
- Mirth globals are missing (
Cannot find name 'ChannelUtil'). Themirth.d.tsfile has to sit inside the project whosenode_modulesholds the package, becausereference typesresolves through that file'snode_moduleschain. To confirm the types load, runnpx tsc -p jsconfig.json --listFilesOnly | grep mirth-connect-types. TS2688: Cannot find type definition file for '<folder>', and nothing else is checked.typeRootspoints at a folder with subfolders, such as atypes/folder of your own declarations. TypeScript treats each subfolder as a type library, and when that fails it skips semantic checking entirely. Set"types": []or removetypeRoots.- Mirth APIs the package covers still show as missing. Nothing in the project references the
package (the
/// <reference types>file is missing or not included), or an older hand-written declaration file is still included and shadows it. Run the--listFilesOnlycheck above. - One parse error, then no other errors at all. TypeScript skips semantic checking when any
file fails to parse, so the check looks clean. Common causes are E4X literals (next entry) and,
on TypeScript 7, Closure-style
{function(A): R}JSDoc. TypeScript 7 also rejectsbaseUrl. - One file with E4X literals stops all checking. XML literal syntax (
var x = <a/>;) is a TypeScript parse error. Exclude those files. TheXMLtype covers the E4X API, not the literal syntax. - Types look like an older version, or hovers show identical duplicate overloads. Two copies
of the package are installed, often after switching package managers. The ambient declarations
merge instead of conflicting. A
jsconfig.jsonproject hasskipLibCheckon by default, and that hides the duplicate-declaration errors, so JavaScript projects get no warning. To check, runnpx tsc -p jsconfig.json --noEmit --skipLibCheck falseand look forTS2300/TS2403errors inmirth-connect-typesfiles. Then runnpm ls @ubercode/mirth-connect-typesorpnpm why @ubercode/mirth-connect-types, and checknode_modules/.pnpm. - Your own code-template globals are
Cannot find name. A file that ends withif (typeof module !== 'undefined') module.exports = X(the usual way to unit-test code templates with Jest) is a CommonJS module to TypeScript, soXis no longer global. Declare it in a.d.ts:declare global { var X: typeof import('./path/to/X'); } export {}; Cannot use namespace 'org' as a value. Rhino also accepts bare third-party roots (org.apache.http…), but the types only cover them throughPackages(Packages.org.apache.http…, typedany). A globalorgvalue would clash with scripts that name a variableorg.- A Mirth internal class fails in a JSDoc type. Internals such as
com.mirth.connect.server.controllers.ControllerFactoryaren't the User API, so they're typed asanyvalues only. In JSDoc, use{any}. Value of type 'typeof X' is not callable. Rhino also constructs a Java object when a class is called withoutnew(java.lang.String('x')), but the types only modelnew. Addnew; it behaves the same.Cannot find name 'console'. That's correct: Mirth's Rhino scope has noconsole. Uselogger. Don't add thedomlib to silence it.
Reporting a type gap
If the types reject code that runs in Mirth, or miss an API, run this in your project:
npx mirth-types-report --out mirth-types-report.md # add --no-source to leave out code linesIt type-checks the project with your own TypeScript and config, and keeps only the errors that
involve this package. Your own code's errors are left out, and unknown names get their own list.
It also flags setup problems: the package not loaded, two copies installed, TypeScript 7, or a
parse error stopping all checks. Review the file, then open a
type gap issue
with it. Options: --project <config> (default jsconfig.json, then tsconfig.json) and
--typescript <dir> to use a TypeScript install outside the project.
Versioning
Definitions are organized per product + per Mirth version:
nextgen-connect/v4.5.2/ ← current target
index.d.ts ← entry; triple-slash references the split files
globals/ ← index.d.ts: msg, tmp, $c/$co/..., maps, helpers, XML;
← rhino.d.ts: Packages, JavaAdapter, importPackage, XMLList;
← userapi.d.ts (generated: ChannelUtil/AttachmentUtil/Status/... globals)
java/ ← java.lang / util / io / time / text / security / ...; primitive
← aliases; coercion.d.ts (JString, JInteger, JKey, ...)
javax/ ← javax.crypto / sql / xml.bind
org/ ← org.dcm4che2
com/mirth/ ← com.mirth.connect.* (userutil, donkey, plugins, internals)What's covered
The three User API packages are generated from the Mirth Javadoc:
| Package | Types | File |
| --------------------------------------------- | ----: | ---------------------------------------- |
| com.mirth.connect.server.userutil | 30 | com/mirth/connect-server-userutil.d.ts |
| com.mirth.connect.userutil | 17 | com/mirth/connect-userutil.d.ts |
| com.mirth.connect.plugins.httpauth.userutil | 2 | com/mirth/plugins.d.ts |
Hand-maintained files cover the rest of what channel scripts touch:
- Mirth script scope, checked against Mirth 4.5.2's
JavaScriptScopeUtilandJavaScriptBuilder:msg/tmp, the maps (includingconfigurationMap) and$accessors,logger,router,alerts,destinationSet,connectorMessage,message,responseand the response globals,connector,template, the attachment and segment helpers, and the E4XXML/XMLList/Namespace/QNameAPI. - Script-specific variables:
readerand the Delimited settings (batch scripts),binary(attachment script), andresultMap(Database Reader update script). They're declared everywhere, because the types can't tell which script a file is. The Database Reader'sresultsis left out, because a global with that name would clash with scripts that declare their ownresults. - Rhino:
Packages,JavaAdapter,importPackage, andJSON.parseof Java strings. - JDK classes scripts use directly:
java.lang(boxed types,String,System,Thread,reflect.Array),java.util(collections,Base64,Arrays,UUID,Calendar,Properties),java.iostreams and writers,java.time,java.text,java.sql/javax.sql,java.security,javax.crypto, andjavax.xml.bind.DatatypeConverter(bundled with Mirth). - Mirth internals: the donkey
Message/ConnectorMessagegetters. Other internals (ControllerFactory,ObjectXMLSerializer, …) resolve as untypedanyvalues.
None of this is exhaustive. If your code uses something missing, report it.
The npm package version tracks this repo's releases (semver); the Mirth version a set of
types targets is encoded in the path/subpath export. Additional versions and products
(Open Integration Engine, BridgeLink) slot in under the same scheme.
How the types are produced
The three userutil files are fully generated from the Javadoc — they carry
the JSDoc, @param/@returns/@throws/@deprecated tags, and signatures the
Javadoc documents. Do not hand-edit them; they are overwritten by
pnpm run generate.
- Fetch the User API Javadoc from a running Mirth container (see
docker-composein the companion repo) intojavadoc/<product>/<version>/— the reproducible source of truth. The HTML is committed, so regeneration does not require a container. - Generate the three userutil
.d.ts(plusglobals/userapi.d.ts, which exposes every User API class/enum as an unqualified global alias) from the Javadoc viasrc/generator/. The generator is deterministic: members are sorted and output is run through Prettier, so re-running produces byte-identical files. - Apply the Rhino coercion rule. Parameters accept the JS values Rhino converts for
them: a JS string for
String, a JS number forInteger/Long/…, anything forObject, a JS array forList/Collection, a JS object forMap. The aliases live injava/coercion.d.ts; return types stay the exact Java type.pnpm run check:coercionenforces the rule on the hand-written files too. - Curate via overlays. Hand-written
@examplesnippets and extra prose for the hot-path classes/methods (ChannelUtil,AttachmentUtil,DateUtil,FileUtil,HTTPUtil,VMRouter,DatabaseConnection*,Lists/Maps, …) live insrc/generator/overlays.ts, keyed byClassName/ClassName#methodName. The emitter merges them into the generated JSDoc, so curation survives regeneration. An overlay can also replace a return type the Javadoc can't express (e.g.ImmutableMessage#getConnectorMessages).
pnpm run fetch-javadoc # pull Javadoc HTML from the container (only when refreshing a version)
pnpm run generate # Javadoc HTML + overlays -> the three userutil .d.ts
pnpm run generate:hash # sha256 of the generated files (idempotency check)
pnpm run check # lint + typecheck + coercion rule + type tests + format + consumer smokeScripts
| Script | Purpose |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| check | lint + typecheck + check:coercion + test:types + format + test:consumer (CI gate). |
| check:coercion | Fail if any declaration parameter breaks the Rhino coercion rule; --fix rewrites them. |
| typecheck | tsc --noEmit over the published .d.ts. |
| test:types | Compile the test-types/ assertions against the definitions. |
| test:consumer | Pack the tarball, install it in a temp project, type-check a real Mirth script, and run the installed mirth-types-report against known package and user errors. |
| lint / format | ESLint (generator code) / Prettier (everything). |
| fetch-javadoc / generate | Regenerate the three userutil files from a Mirth container's Javadoc. |
| generate:hash | Print the sha256 of each generated file (byte-idempotency check). |
Project status & roadmap
The types target NextGen Connect 4.5.2 only. Parameter and collection typing follows what
Mirth's Rhino actually does, verified by running probes in the 4.5.2 image. The JDK and internal
coverage grows from what real projects report through mirth-types-report.
Planned next:
- More Mirth Connect versions — generated from each release's Javadoc and published under the
same
nextgen-connect/v<x.y.z>subpath scheme, so you can pin types per environment. - Forks — Open Integration Engine and BridgeLink, under their own
open-integration-engine/…andbridgelink/…subpaths. - More of the JDK and Mirth internals, driven by type-gap reports.
License
MIT © Michael Hobbs
