@hla4ts/fom-codegen 0.1.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,247 @@
1
+ # @hla4ts/fom-codegen
2
+
3
+ **TypeScript-first FOM/SOM code generator (IEEE 1516-2025 OMT/FDD XML)**
4
+
5
+ This package provides a registry API for describing object/interaction classes and data types in TypeScript, then generating valid IEEE 1516-2025 XML (OMT or FDD). It is designed to be the base for higher-level SpaceFOM-friendly frameworks.
6
+
7
+ ## Goals
8
+
9
+ - Let users describe HLA classes in TypeScript without hand-editing XML.
10
+ - Generate deterministic, schema-valid XML for import into RTIs.
11
+ - Keep metadata explicit (update types, ownership, transport/order, semantics).
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ bun add @hla4ts/fom-codegen
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```ts
22
+ import {
23
+ FomRegistry,
24
+ type ObjectClassSchema,
25
+ } from "@hla4ts/fom-codegen";
26
+
27
+ const registry = new FomRegistry();
28
+
29
+ registry.setModelIdentification({
30
+ name: "MyFederate FOM",
31
+ type: "FOM",
32
+ version: "0.1.0",
33
+ modificationDate: "2026-01-22",
34
+ securityClassification: "Unclassified",
35
+ description: "Custom SpaceFOM extensions for MyFederate",
36
+ });
37
+
38
+ const vehicle: ObjectClassSchema = {
39
+ name: "SpaceVehicle",
40
+ parent: "PhysicalEntity",
41
+ sharing: "PublishSubscribe",
42
+ semantics: "A user-defined vehicle class.",
43
+ attributes: [
44
+ {
45
+ name: "name",
46
+ dataType: "HLAunicodeString",
47
+ updateType: "Static",
48
+ updateCondition: "during initialization",
49
+ ownership: "NoTransfer",
50
+ sharing: "PublishSubscribe",
51
+ transportation: "HLAreliable",
52
+ order: "TimeStamp",
53
+ semantics: "Unique name.",
54
+ valueRequired: true,
55
+ },
56
+ ],
57
+ };
58
+
59
+ registry.addObjectClass(vehicle);
60
+
61
+ const xml = registry.toXml({ format: "omt" });
62
+ console.log(xml);
63
+ ```
64
+
65
+ ## Sequence Diagram
66
+
67
+ ```mermaid
68
+ sequenceDiagram
69
+ participant User as Developer
70
+ participant Registry as FomRegistry
71
+ participant Writer as renderFomXml
72
+ User->>Registry: addObjectClass / addDataType
73
+ User->>Registry: toXml({ format })
74
+ Registry->>Writer: buildModel()
75
+ Writer-->>User: OMT/FDD XML
76
+ ```
77
+
78
+ ## Registry API
79
+
80
+ - `setModelIdentification(...)`
81
+ - `setTime(...)`
82
+ - `addObjectClass(...)`
83
+ - `addInteractionClass(...)`
84
+ - `addDataType(...)`
85
+ - `addDimension(...)`
86
+ - `addTransportation(...)`
87
+ - `setSwitches(...)`
88
+ - `toXml({ format: "omt" | "fdd" })`
89
+
90
+ ## XML Parsing
91
+
92
+ You can parse an existing XML FOM into a `FomModel` and then re-emit or convert it:
93
+
94
+ ```ts
95
+ import { parseFomXml, generateFomTypescript } from "@hla4ts/fom-codegen";
96
+
97
+ const model = parseFomXml(xmlString);
98
+ const ts = generateFomTypescript(model, { mode: "registry" });
99
+ ```
100
+
101
+ ## XSD-Based Type Generation
102
+
103
+ This package includes TypeScript types generated directly from the IEEE 1516-2025 XSD schemas. These types provide:
104
+
105
+ - **Autocomplete** based on the official XML schema
106
+ - **Type checking** that matches XSD validation
107
+ - **Documentation** extracted from XSD annotations
108
+
109
+ ### Using XSD Types
110
+
111
+ The generated types are available from the `generated` submodule:
112
+
113
+ ```ts
114
+ import type {
115
+ ModelIdentification,
116
+ ObjectClass,
117
+ InteractionClass,
118
+ SharingEnumeration,
119
+ OrderEnumeration,
120
+ } from "@hla4ts/fom-codegen/generated";
121
+
122
+ // Use XSD types for autocomplete
123
+ const modelId: ModelIdentification = {
124
+ name: "MyFOM",
125
+ type: "FOM", // Autocomplete: "FOM" | "SOM"
126
+ version: "1.0",
127
+ modificationDate: "2026-01-23",
128
+ securityClassification: "Unclassified", // Autocomplete shows valid values
129
+ description: "My federation object model",
130
+ };
131
+
132
+ const objectClass: ObjectClass = {
133
+ name: "MyObject",
134
+ sharing: "PublishSubscribe", // Autocomplete: "Publish" | "Subscribe" | "PublishSubscribe" | "Neither"
135
+ // ... other properties with full autocomplete
136
+ };
137
+ ```
138
+
139
+ ### Generating Types
140
+
141
+ Types are generated from the XSD schemas in the `schema/` directory:
142
+
143
+ ```bash
144
+ bun run generate-types
145
+ ```
146
+
147
+ This parses `IEEE1516-DIF-2025.xsd` and generates TypeScript types in `src/generated/xsd-types.ts`.
148
+
149
+ ### Type Adapters
150
+
151
+ Use the adapter functions to convert XSD types to FomRegistry schema types:
152
+
153
+ ```ts
154
+ import {
155
+ FomRegistry,
156
+ adaptModelIdentification,
157
+ adaptObjectClass,
158
+ adaptInteractionClass,
159
+ } from "@hla4ts/fom-codegen";
160
+ import type {
161
+ ModelIdentification,
162
+ ObjectClass,
163
+ InteractionClass,
164
+ } from "@hla4ts/fom-codegen/generated";
165
+
166
+ const registry = new FomRegistry();
167
+
168
+ // Define using XSD types for autocomplete and type checking
169
+ const modelId: ModelIdentification = {
170
+ name: "MyFOM",
171
+ type: "FOM", // TypeScript autocomplete: "FOM" | "SOM"
172
+ version: "1.0",
173
+ modificationDate: "2026-01-23",
174
+ securityClassification: "Unclassified", // Autocomplete shows valid values
175
+ description: "My federation object model",
176
+ };
177
+
178
+ // Convert and use with FomRegistry
179
+ registry.setModelIdentification(adaptModelIdentification(modelId));
180
+
181
+ // Object classes with XSD autocomplete
182
+ const vehicle: ObjectClass = {
183
+ name: "SpaceVehicle",
184
+ sharing: "PublishSubscribe", // Autocomplete: "Publish" | "Subscribe" | "PublishSubscribe" | "Neither"
185
+ semantics: "A space vehicle",
186
+ attribute: [
187
+ {
188
+ name: "name",
189
+ dataType: "HLAunicodeString",
190
+ updateType: "Static", // Autocomplete: "Static" | "Periodic" | "Conditional" | "NA"
191
+ ownership: "NoTransfer", // Autocomplete: "Divest" | "Acquire" | "DivestAcquire" | "NoTransfer"
192
+ sharing: "PublishSubscribe",
193
+ transportation: "HLAreliable",
194
+ order: "TimeStamp", // Autocomplete: "Receive" | "TimeStamp"
195
+ },
196
+ ],
197
+ };
198
+
199
+ registry.addObjectClass(adaptObjectClass(vehicle));
200
+ ```
201
+
202
+ ### Benefits
203
+
204
+ - **Compile-time type checking**: TypeScript will catch invalid enum values, missing required fields, and type mismatches
205
+ - **Autocomplete**: Your IDE will suggest valid values for enums and available properties
206
+ - **XSD alignment**: The generated types match the XSD schema exactly, so what TypeScript accepts will pass XSD validation
207
+ - **Documentation**: JSDoc comments from XSD annotations are preserved in the generated types
208
+
209
+ ### Parser Notes
210
+
211
+ - The parser targets IEEE 1516-2025 OMT/FDD structures and ignores unrelated XML.
212
+ - Namespaces are stripped from element and attribute names.
213
+ - Missing attribute metadata is defaulted to safe values (e.g., `updateType = "NA"`).
214
+
215
+ ## Notes
216
+
217
+ - OMT output requires `modelIdentification` fields. FDD output is more relaxed.
218
+ - The generator always writes `HLAobjectRoot` and `HLAinteractionRoot` if not provided.
219
+ - Attribute/interaction metadata is not inferred from TS types; it must be explicit.
220
+
221
+ ## Testing
222
+
223
+ ```bash
224
+ cd packages/fom-codegen
225
+ bun test
226
+ ```
227
+
228
+ ## CLI
229
+
230
+ Generate XML from a registry module:
231
+
232
+ ```bash
233
+ bun run @hla4ts/fom-codegen cli -- --entry src/fom.ts --out dist/fom.xml --format omt
234
+ ```
235
+
236
+ Entry modules can export:
237
+
238
+ - `registry`: a `FomRegistry` instance
239
+ - `default`: a `FomRegistry` instance
240
+ - `buildFom()`: function returning `FomModel`
241
+ - `default`: a `FomModel` object
242
+
243
+ Parse XML and emit TypeScript:
244
+
245
+ ```bash
246
+ bun run @hla4ts/fom-codegen cli -- --from-xml spacefom/SISO_SpaceFOM_entity.xml --out-ts src/fom.ts --ts-mode registry
247
+ ```
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@hla4ts/fom-codegen",
3
+ "version": "0.1.0",
4
+ "description": "FOM XML parser, registry, and TypeScript code generation tooling for hla4typescript",
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "types": "src/index.ts",
8
+ "bin": {
9
+ "hla4ts-fom": "src/cli.ts"
10
+ },
11
+ "files": [
12
+ "README.md",
13
+ "src"
14
+ ],
15
+ "exports": {
16
+ ".": "./src/index.ts"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "dependencies": {},
22
+ "scripts": {
23
+ "cli": "bun run src/cli.ts",
24
+ "typecheck": "tsc --noEmit",
25
+ "test": "bun test",
26
+ "generate-types": "bun run scripts/generate-types.ts"
27
+ },
28
+ "license": "MIT"
29
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env bun
2
+ import { resolve } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { readFileSync, writeFileSync } from "node:fs";
5
+ import { FomRegistry } from "./registry.ts";
6
+ import type { FomModel, FomOutputFormat, FomOutputOptions } from "./types.ts";
7
+ import { renderFomXml } from "./xml-writer.ts";
8
+ import { parseFomXml } from "./xml-parser.ts";
9
+ import { generateFomTypescript } from "./ts-codegen.ts";
10
+
11
+ type CliArgs = {
12
+ entry: string;
13
+ out?: string;
14
+ format: FomOutputFormat;
15
+ schemaLocation?: string;
16
+ includeModelIdentification: boolean;
17
+ indent?: string;
18
+ fromXml?: string;
19
+ outTs?: string;
20
+ tsMode?: "model" | "registry";
21
+ };
22
+
23
+ function parseArgs(argv: string[]): CliArgs {
24
+ const args: CliArgs = {
25
+ entry: "",
26
+ format: "omt",
27
+ includeModelIdentification: true,
28
+ };
29
+
30
+ for (let i = 0; i < argv.length; i += 1) {
31
+ const arg = argv[i];
32
+ switch (arg) {
33
+ case "--entry":
34
+ args.entry = argv[++i] ?? "";
35
+ break;
36
+ case "--from-xml":
37
+ args.fromXml = argv[++i];
38
+ break;
39
+ case "--out-ts":
40
+ args.outTs = argv[++i];
41
+ break;
42
+ case "--ts-mode":
43
+ args.tsMode = argv[++i] as "model" | "registry";
44
+ break;
45
+ case "--out":
46
+ args.out = argv[++i];
47
+ break;
48
+ case "--format":
49
+ args.format = (argv[++i] as FomOutputFormat) ?? "omt";
50
+ break;
51
+ case "--schema":
52
+ args.schemaLocation = argv[++i];
53
+ break;
54
+ case "--no-model-identification":
55
+ args.includeModelIdentification = false;
56
+ break;
57
+ case "--indent":
58
+ args.indent = argv[++i];
59
+ break;
60
+ case "--fdd":
61
+ args.format = "fdd";
62
+ break;
63
+ case "--omt":
64
+ args.format = "omt";
65
+ break;
66
+ case "--help":
67
+ printUsage();
68
+ process.exit(0);
69
+ default:
70
+ if (!arg.startsWith("--") && !args.entry) {
71
+ args.entry = arg;
72
+ }
73
+ break;
74
+ }
75
+ }
76
+
77
+ if (!args.entry && !args.fromXml) {
78
+ printUsage();
79
+ process.exit(1);
80
+ }
81
+
82
+ return args;
83
+ }
84
+
85
+ function printUsage(): void {
86
+ const text = [
87
+ "Usage: hla4ts-fom --entry <file> [options]",
88
+ "",
89
+ "Options:",
90
+ " --entry <file> Module that exports a registry or model",
91
+ " --out <file> Output XML file (stdout if omitted)",
92
+ " --format <omt|fdd> Output format (default: omt)",
93
+ " --omt Shortcut for --format omt",
94
+ " --fdd Shortcut for --format fdd",
95
+ " --from-xml <file> Parse XML and emit TypeScript",
96
+ " --out-ts <file> Output TypeScript file",
97
+ " --ts-mode <model|registry> TypeScript output mode (default: model)",
98
+ " --schema <value> Override xsi:schemaLocation",
99
+ " --indent <value> Indent string (default: two spaces)",
100
+ " --no-model-identification Skip modelIdentification block",
101
+ ];
102
+ console.log(text.join("\n"));
103
+ }
104
+
105
+ function resolveModel(mod: Record<string, unknown>): FomModel {
106
+ if (isRegistry(mod.registry)) {
107
+ return (mod.registry as FomRegistry).buildModel();
108
+ }
109
+ if (isRegistry(mod.default)) {
110
+ return (mod.default as FomRegistry).buildModel();
111
+ }
112
+ if (typeof mod.buildFom === "function") {
113
+ return mod.buildFom() as FomModel;
114
+ }
115
+ if (isFomModel(mod.default)) {
116
+ return mod.default as FomModel;
117
+ }
118
+ if (isFomModel(mod.fom)) {
119
+ return mod.fom as FomModel;
120
+ }
121
+ throw new Error("Entry module did not export a registry or FomModel.");
122
+ }
123
+
124
+ function isRegistry(value: unknown): value is FomRegistry {
125
+ return Boolean(
126
+ value &&
127
+ typeof value === "object" &&
128
+ typeof (value as FomRegistry).buildModel === "function" &&
129
+ typeof (value as FomRegistry).toXml === "function"
130
+ );
131
+ }
132
+
133
+ function isFomModel(value: unknown): value is FomModel {
134
+ return Boolean(
135
+ value &&
136
+ typeof value === "object" &&
137
+ Array.isArray((value as FomModel).objectClasses) &&
138
+ Array.isArray((value as FomModel).interactionClasses) &&
139
+ Array.isArray((value as FomModel).dataTypes)
140
+ );
141
+ }
142
+
143
+ async function main(): Promise<void> {
144
+ const args = parseArgs(process.argv.slice(2));
145
+ if (args.fromXml) {
146
+ const xml = readFileSync(args.fromXml, "utf8");
147
+ const model = parseFomXml(xml);
148
+ const ts = generateFomTypescript(model, {
149
+ mode: args.tsMode ?? "model",
150
+ });
151
+ if (args.outTs) {
152
+ writeFileSync(args.outTs, ts);
153
+ } else {
154
+ console.log(ts);
155
+ }
156
+ return;
157
+ }
158
+
159
+ const entryUrl = pathToFileURL(resolve(args.entry)).href;
160
+ const mod = (await import(entryUrl)) as Record<string, unknown>;
161
+ const model = resolveModel(mod);
162
+ const options: FomOutputOptions = {
163
+ format: args.format,
164
+ schemaLocation: args.schemaLocation,
165
+ includeModelIdentification: args.includeModelIdentification,
166
+ indent: args.indent,
167
+ };
168
+ const xml = renderFomXml(model, options);
169
+ if (args.out) {
170
+ writeFileSync(args.out, xml);
171
+ } else {
172
+ console.log(xml);
173
+ }
174
+ }
175
+
176
+ main().catch((error) => {
177
+ console.error(`[hla4ts-fom] ${error instanceof Error ? error.message : String(error)}`);
178
+ process.exit(1);
179
+ });
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Generated XSD Types - Re-exported for convenience
3
+ *
4
+ * This module re-exports the generated TypeScript types from the XSD schema.
5
+ * These types provide autocomplete and type checking that matches XSD validation.
6
+ */
7
+
8
+ export * from "./xsd-types.ts";
9
+
10
+ // Re-export commonly used types with shorter names for convenience
11
+ export type {
12
+ ModelIdentification as XsdModelIdentification,
13
+ ObjectClass as XsdObjectClass,
14
+ InteractionClass as XsdInteractionClass,
15
+ Attribute as XsdAttribute,
16
+ Parameter as XsdParameter,
17
+ } from "./xsd-types.ts";
18
+
19
+ export type {
20
+ SharingEnumeration as XsdSharingType,
21
+ OrderEnumeration as XsdOrderType,
22
+ UpdateEnumeration as XsdUpdateType,
23
+ OwnershipEnumeration as XsdOwnershipType,
24
+ OMTypeEnumeration as XsdOMType,
25
+ } from "./xsd-types.ts";