@3dsource/unreal-commands-api-parser
v1.1.3
Published
Generates TypeScript definitions from the 3DSource METABOX commands API documentation
Readme
@3dsource/unreal-commands-api-parser
Generates strongly-typed TypeScript definitions from the 3DSource METABOX
commands API documentation. Fetches the live API doc (JSON) and emits a single
struct.ts containing enums, interfaces, command/callback unions and Unreal
type aliases — so your app can talk to the Unreal MetaBox runtime type-safely.
Installation
pnpm add -D @3dsource/unreal-commands-api-parserUsage
The package ships a unreal-commands-api-parser binary. Point it at the API doc
URL and the desired output path:
{
"scripts": {
"getStruct": "unreal-commands-api-parser \"https://preview.3dsource.com/vps-commands-api/documentation/latest\" \"src/struct.ts\""
}
}pnpm getStructThis writes src/struct.ts (the typings) and src/struct.ts.json (the raw
API dump, kept for debugging — safe to gitignore).
--help prints the usage block, --version the parser version.
The CLI exits with a non-zero code — so it can gate a CI step — when the
arguments are missing, the fetch fails, or the endpoint answers 200 with
something that is not the API document (a proxy error page, a login redirect).
That last check exists so a bad response fails loudly instead of quietly
producing an empty struct.ts.
If Prettier is resolvable from your project, the output is formatted with your config; if it is absent, the parser warns and writes unformatted-but-valid output. A Prettier that is present and rejects the output is treated as a parser bug and fails the run.
Inside this monorepo
@3dsource/types-unreal consumes the parser straight from source — the linked
workspace package contains src/, not the built dist/:
pnpm types-unreal:generate:commands:prod # tsx ../unreal-commands-api-parser/src/cli.ts …Programmatic use
The package root has no side effects and exposes the generator:
import { ParseApi, type DATAStructureContainer } from '@3dsource/unreal-commands-api-parser';
// Pure: takes a parsed API document, returns the TypeScript source.
const apiDoc = (await (await fetch(url)).json()) as DATAStructureContainer;
const typings: string = ParseApi.generate(apiDoc);
// Or let it do the I/O: fetch, validate, format, write both files.
await ParseApi.run(url, 'src/struct.ts');Also exported: the inheritance helpers used to classify structs
(buildInheritanceIndex, isCommandStruct, isCallbackStruct,
isDescendantOf), parseDocComments, getAliasesMap/Aliases, and the
typings for the incoming API JSON.
dist/index.jsstill runs the CLI for consumers pinned to the pre-1.1.3 layout, but it is deprecated — use the binary.
What gets generated
For every struct and enum in the API doc, struct.ts contains a matching
export interface / export enum. On top of that, two parallel sets of helper
types are produced — one for commands (sent to Unreal) and one for
callbacks (received from Unreal):
| Commands (outgoing) | Callbacks (incoming) |
| -------------------------------- | --------------------------------- |
| MetaBoxCommand (enum) | MetaBoxCallback (enum) |
| MetaBoxCommandList (interface) | MetaBoxCallbackList (interface) |
| UnrealCommands (union) | UnrealCallbacks (union) |
| MetaBoxCommandPacket | MetaBoxCallbackPacket |
| DeprecatedCommands (const) | DeprecatedCallbacks (const) |
Plus:
TelemetryData—{ trackingId?: string }, intersected into both packet types.- Unreal type aliases:
FString → string,int32/float → number,bool → boolean,TArray<T> → T[],TMap<T,K> → Map<T,K>.
Commands vs callbacks
- Command — a struct descending from
FCommandBase. You build it and send it to Unreal. Discriminated bycommand: MetaBoxCommand.X. - Callback — a struct descending from
FCallbackBase. Unreal emits it. Discriminated bycallback: MetaBoxCallback.X. Two flavours:- spontaneous (
FCallbackBasedirectly) — Unreal emits it without a preceding command (e.g.FOnFocusObjectCallback). - command response (
FCommandCallbackBase) — the reply to a command you sent. These additionally carrycorrelationId: stringandtrackingId: stringso you can match a response back to its request.
- spontaneous (
Example — sending a command
import { MetaBoxCommand, type UnrealCommands, type MetaBoxCommandPacket } from './struct';
const command: UnrealCommands = {
command: MetaBoxCommand.F360EnableCommand,
payload: {
value: true,
armLength: 0,
},
};
function sendCommandToUnreal(data: MetaBoxCommandPacket): void {
// ... transport to Unreal
console.log(data);
}
sendCommandToUnreal(command);Example — handling a callback
import { MetaBoxCallback, type MetaBoxCallbackPacket } from './struct';
function onUnrealCallback(data: MetaBoxCallbackPacket): void {
switch (data.callback) {
case MetaBoxCallback.FOnFocusObjectCallback:
// data.payload is narrowed to the focus-object payload
console.log('focused', data.payload);
break;
// command responses carry correlationId / trackingId to match the request
default:
break;
}
}Generating documentation
The output is fully TSDoc-annotated, so TypeDoc can build browsable docs:
{
"scripts": {
"doc": "typedoc --out dist/docs src/struct.ts"
}
}Inside this monorepo the generated types-unreal typings are documented by
pnpm types-unreal:docs.
Development
Run from the workspace root:
pnpm unreal-commands-api-parser:typecheck # tsc --noEmit
pnpm unreal-commands-api-parser:test # vitest (snapshot + behaviour)
pnpm unreal-commands-api-parser:lint # ng lint
pnpm unreal-commands-api-parser:build # tsc → dist/3dsource/unreal-commands-api-parserRelease (bumps the version, builds, validates, packs, smoke-tests, publishes):
pnpm unreal-commands-api-parser:release:patchProject layout
src/
public-api.ts library entry: ParseApi + typings, no side effects
cli.ts CLI entry: argv → ParseApi.run(url, output)
index.ts deprecated CLI shim for the pre-1.1.3 dist layout
parser.spec.ts snapshot + classification tests over the fixture
core/
ParseApi.ts orchestration: fetch → validate → generate → write
BaseDoc.ts generic template-method base for emitters
Enums.ts emits `export enum`
Properties.ts emits `export interface` + discriminators
CommandPacket.ts emits MetaBoxCommand / UnrealCommands / …
CallbackPacket.ts emits MetaBoxCallback / UnrealCallbacks / …
TsDocFragment.ts builds TSDoc blocks from doc_comments
Aliases.ts Unreal → TS primitive aliases
ParseApi.spec.ts fetch, response validation, file writing
generate.spec.ts paths the fixture misses: deprecated callbacks,
TSDoc tags
helpers/
inheritance.ts inheritance index + command/callback classification
parseDocComments.ts tolerant doc_comments JSON parser
getShortCommandName.ts enum-name helpers
clean.ts indentation/usage-link helpers
helpers.spec.ts unit tests for the above
interfaces/ typings for the incoming API JSON
__fixtures__/sample.json representative API doc
__snapshots__/ committed generator outputCommands vs callbacks are classified by walking the inheritance chain
(helpers/inheritance.ts), not by name matching.
Build contract
scripts/build.mjs compiles src/ with tsc into
dist/3dsource/unreal-commands-api-parser/, writes the publishable manifest
there (source manifest minus scripts and devDependencies), copies
README.md and LICENSE, and adds the shebang to cli.js. The workspace
build, validation, packing and release scripts in scripts/packages/ then treat
this package like any other.
main, types, exports and bin in package.json describe the built
layout, so those paths do not exist in the source directory — the same
convention @3dsource/data-loader uses. Two consequences:
cli.jsin the package root is a stub that pnpm can link as the workspace binary; it forwards to the built CLI and tells you to build if it is missing.- Local consumers resolve the package through the alias in
vitest.config.tsandpathsin the roottsconfig.json, never through the source manifest.
The snapshot in src/__snapshots__/ is the committed source of truth for the
generated output. Update it with pnpm unreal-commands-api-parser:test -- -u
only when a generator change is intentional, and read the resulting diff — an
unexpected line there means the change reached further than intended.
