diff --git a/CLAUDE.md b/CLAUDE.md
index 4d104f50..37e660f3 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -85,7 +85,6 @@ This is a yarn workspace with these packages:
- `@system-dynamics/app` - Frontend application
- `@system-dynamics/server` - Backend API server
- `@system-dynamics/engine2` - WASM simulation engine
-- `@system-dynamics/xmutil` - WASM XML utilities
### Prerequisites for Development
diff --git a/examples/serialize.mjs b/examples/serialize.mjs
index b974e024..bdca3a40 100644
--- a/examples/serialize.mjs
+++ b/examples/serialize.mjs
@@ -2,7 +2,6 @@ import { readFileSync, createWriteStream } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
-import { convertMdlToXmile } from '@system-dynamics/xmutil';
import { Project as Engine2Project } from '@system-dynamics/engine2';
// Compute the WASM path relative to the engine2 package
@@ -14,11 +13,9 @@ const args = process.argv.slice(2);
const inputFile = args[0];
let contents = readFileSync(args[0], 'utf-8');
-if (inputFile.endsWith('.mdl')) {
- contents = await convertMdlToXmile(contents, false);
-}
-
-const project = await Engine2Project.open(contents, { wasm: wasmPath });
+const project = inputFile.endsWith('.mdl')
+ ? await Engine2Project.openVensim(contents, { wasm: wasmPath })
+ : await Engine2Project.open(contents, { wasm: wasmPath });
const pb = project.serializeProtobuf();
const outputFile = createWriteStream(args[1]);
diff --git a/examples/svg.mjs b/examples/svg.mjs
index 446d533f..d194a020 100644
--- a/examples/svg.mjs
+++ b/examples/svg.mjs
@@ -2,7 +2,6 @@ import { readFileSync, createWriteStream } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
-import { convertMdlToXmile } from '@system-dynamics/xmutil';
import { Project as Engine2Project } from '@system-dynamics/engine2';
import { Project } from '@system-dynamics/core/datamodel';
import { renderSvgToString } from '@system-dynamics/diagram/render-common';
@@ -16,11 +15,9 @@ const args = process.argv.slice(2);
const inputFile = args[0];
let contents = readFileSync(args[0], 'utf-8');
-if (inputFile.endsWith('.mdl')) {
- contents = await convertMdlToXmile(contents, false);
-}
-
-const engine2Project = await Engine2Project.open(contents, { wasm: wasmPath });
+const engine2Project = inputFile.endsWith('.mdl')
+ ? await Engine2Project.openVensim(contents, { wasm: wasmPath })
+ : await Engine2Project.open(contents, { wasm: wasmPath });
const pb = engine2Project.serializeProtobuf();
const project = Project.deserializeBinary(pb);
diff --git a/package.json b/package.json
index f141bb4d..4711303a 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,6 @@
"main": "src/server/lib",
"workspaces": {
"packages": [
- "src/xmutil-js",
"src/engine2",
"src/core",
"src/diagram",
diff --git a/scripts/debug-diagram-gen.mjs b/scripts/debug-diagram-gen.mjs
index b3b5b12c..8be682cc 100755
--- a/scripts/debug-diagram-gen.mjs
+++ b/scripts/debug-diagram-gen.mjs
@@ -11,7 +11,6 @@ import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';
import { Project as Engine2Project } from '@system-dynamics/engine2';
-import { convertMdlToXmile } from '@system-dynamics/xmutil';
import { Project as ProjectDM } from '@system-dynamics/core/datamodel';
import { renderSvgToString } from '@system-dynamics/diagram/render-common';
@@ -83,22 +82,12 @@ async function main() {
try {
// Read the input file
- let contents = readFileSync(inputFile, 'utf-8');
-
- // Convert Vensim files to XMILE if needed
- if (inputFile.endsWith('.mdl')) {
- const [xmileContents, logs] = await convertMdlToXmile(contents, true);
- if (xmileContents.length === 0) {
- throw new Error('Vensim converter failed: ' + (logs || 'unknown error'));
- }
- contents = xmileContents;
- if (logs) {
- console.log('Conversion logs:', logs);
- }
- }
+ const contents = readFileSync(inputFile, 'utf-8');
- // Import the XMILE content using engine2
- const engine2Project = await Engine2Project.open(contents, { wasm: wasmPath });
+ // Import the content using engine2
+ const engine2Project = inputFile.endsWith('.mdl')
+ ? await Engine2Project.openVensim(contents, { wasm: wasmPath })
+ : await Engine2Project.open(contents, { wasm: wasmPath });
const projectPB = engine2Project.serializeProtobuf();
const project = ProjectDM.deserializeBinary(projectPB);
@@ -111,8 +100,8 @@ async function main() {
// Create a copy of the XMILE file without views
console.log('\nCreating XMILE copy without views...');
- // Use the converted XMILE content if we converted from MDL, otherwise read the original
- let xmileContent = contents;
+ // Get XMILE from the engine project (handles both XMILE and MDL inputs)
+ const xmileContent = engine2Project.toXmileString();
// Remove the ... section using regex
// This regex matches tags with any attributes and all content until the closing
diff --git a/src/xmutil-js/.npmignore b/src/xmutil-js/.npmignore
deleted file mode 120000
index b4359f69..00000000
--- a/src/xmutil-js/.npmignore
+++ /dev/null
@@ -1 +0,0 @@
-../../.npmignore
\ No newline at end of file
diff --git a/src/xmutil-js/LICENSE b/src/xmutil-js/LICENSE
deleted file mode 100644
index 874fc432..00000000
--- a/src/xmutil-js/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright 2020 Bobby Powers
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/src/xmutil-js/README.md b/src/xmutil-js/README.md
deleted file mode 100644
index 0a2d7c16..00000000
--- a/src/xmutil-js/README.md
+++ /dev/null
@@ -1,26 +0,0 @@
-convert [Vensim](https://vensim.com/vensim-software/) mdl files to [XMILE](http://docs.oasis-open.org/xmile/xmile/v1.0/cos01/xmile-v1.0-cos01.html#_Toc426543526)
-=============================================
-
-This is Bob Eberlein's [xmutil](https://github.com/bobeberlein/xmutil)
-project compiled to WebAssembly and wrapped in the simplest possible
-TypeScript wrapper (and also usable from plain JavaScript).
-
-It is usable both the browser -- it should be easily adaptable to node as well (patches welcome!).
-
-Usage
------
-
-```js
-import { convertMdlToXmile } from '@system-dynamics/xmutil';
-
-const args = process.argv.slice(2);
-const mdlFile = fs.readFileSync(args[0], 'utf-8');
-
-let xmile = await convertMdlToXmile(mdlFile, false);
-console.log(xmile);
-```
-
-License
--------
-
-@system-dynamics/xmutil is offered under the MIT license for consistency with the C++ xmutil.
diff --git a/src/xmutil-js/eslint.config.js b/src/xmutil-js/eslint.config.js
deleted file mode 100644
index 635f2314..00000000
--- a/src/xmutil-js/eslint.config.js
+++ /dev/null
@@ -1,11 +0,0 @@
-const { createConfig } = require('../../eslint.config.shared');
-
-module.exports = createConfig({
- project: './tsconfig.json',
- ignorePatterns: [
- 'lib/',
- 'lib.browser/',
- 'xmutil.wasm',
- 'xmutil.wasm.d.ts',
- ],
-});
\ No newline at end of file
diff --git a/src/xmutil-js/index.ts b/src/xmutil-js/index.ts
deleted file mode 100644
index 8b160056..00000000
--- a/src/xmutil-js/index.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2021 The Simlin Authors. All rights reserved.
-// Use of this source code is governed by the Apache License,
-// Version 2.0, that can be found in the LICENSE file.
-
-export function defined(object: T | undefined): T {
- if (object === undefined) {
- throw new Error('expected non-undefined object');
- }
- return object;
-}
-
-let cachedWasmModule: typeof import('./xmutil.wasm') | undefined;
-function getWasmModule(): Promise {
- return new Promise((resolve, reject) => {
- if (cachedWasmModule) {
- resolve(cachedWasmModule);
- return;
- }
-
- import('./xmutil.wasm')
- .then((module) => {
- cachedWasmModule = module;
- resolve(module);
- })
- .catch(reject);
- });
-}
-
-const cachedTextEncoder = new TextEncoder();
-const cachedTextDecoder = new TextDecoder();
-
-let cachegetUint8Memory0: Uint8Array | null = null;
-function getUint8Memory0() {
- const wasm = defined(cachedWasmModule);
- if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
- cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
- }
- return cachegetUint8Memory0;
-}
-
-function getStringFromWasm(ptr: number): string {
- if (ptr === 0) {
- return '';
- }
- const mem = getUint8Memory0();
- let off = 0;
- while (mem[ptr + off] !== 0) {
- off++;
- }
- return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + off));
-}
-
-export async function convertMdlToXmile(
- mdlSource: string | Readonly,
- pretty = true,
-): Promise<[string, string?]> {
- if (typeof mdlSource === 'string') {
- mdlSource = cachedTextEncoder.encode(mdlSource);
- }
-
- const wasm = await getWasmModule();
-
- wasm.xmutil_clear_log();
-
- const mdlSourcePtr = wasm.malloc(mdlSource.length);
- getUint8Memory0()
- .subarray(mdlSourcePtr, mdlSourcePtr + mdlSource.length)
- .set(mdlSource);
-
- const resultPtr = wasm.xmutil_convert_mdl_to_xmile(mdlSourcePtr, mdlSource.length, 0, !pretty, false, true);
- const result = getStringFromWasm(resultPtr);
- wasm.free(resultPtr);
-
- const logPtr = wasm.xmutil_get_log();
- let log: string | undefined = getStringFromWasm(logPtr);
- if (!log) {
- log = undefined;
- }
-
- wasm.xmutil_clear_log();
-
- return [result, log];
-}
diff --git a/src/xmutil-js/index_main.ts b/src/xmutil-js/index_main.ts
deleted file mode 100644
index ee878cea..00000000
--- a/src/xmutil-js/index_main.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2021 The Simlin Authors. All rights reserved.
-// Use of this source code is governed by the Apache License,
-// Version 2.0, that can be found in the LICENSE file.
-
-import { promises as fs } from 'fs';
-import { join } from 'path';
-
-export function defined(object: T | undefined): T {
- if (object === undefined) {
- throw new Error('expected non-undefined object');
- }
- return object;
-}
-
-let cachedWasmModule: typeof import('./xmutil.wasm') | undefined;
-function getWasmModule(): Promise {
- return new Promise((resolve, reject) => {
- if (cachedWasmModule) {
- resolve(cachedWasmModule);
- return;
- }
-
- fs.readFile(join(__dirname, 'xmutil.wasm'))
- .then((contents) => {
- WebAssembly.instantiate(contents as BufferSource)
- .then((source) => {
- cachedWasmModule = source.instance.exports as unknown as typeof import('./xmutil.wasm');
- resolve(cachedWasmModule);
- })
- .catch(reject);
- })
- .catch(reject);
- });
-}
-
-const cachedTextEncoder = new TextEncoder();
-const cachedTextDecoder = new TextDecoder();
-
-let cachegetUint8Memory0: Uint8Array | null = null;
-function getUint8Memory0() {
- const wasm = defined(cachedWasmModule);
- if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
- cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
- }
- return cachegetUint8Memory0;
-}
-
-function getStringFromWasm(ptr: number): string {
- if (ptr === 0) {
- return '';
- }
- const mem = getUint8Memory0();
- let off = 0;
- while (mem[ptr + off] !== 0) {
- off++;
- }
- return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + off));
-}
-
-export async function convertMdlToXmile(
- mdlSource: string | Readonly,
- pretty = true,
-): Promise<[string, string?]> {
- if (typeof mdlSource === 'string') {
- mdlSource = cachedTextEncoder.encode(mdlSource);
- }
-
- const wasm = await getWasmModule();
-
- wasm.xmutil_clear_log();
-
- const mdlSourcePtr = wasm.malloc(mdlSource.length);
- getUint8Memory0()
- .subarray(mdlSourcePtr, mdlSourcePtr + mdlSource.length)
- .set(mdlSource);
-
- const resultPtr = wasm.xmutil_convert_mdl_to_xmile(mdlSourcePtr, mdlSource.length, 0, !pretty, false, false);
- const result = getStringFromWasm(resultPtr);
- wasm.free(resultPtr);
-
- const logPtr = wasm.xmutil_get_log();
- let log: string | undefined = getStringFromWasm(logPtr);
- if (!log) {
- log = undefined;
- }
-
- wasm.xmutil_clear_log();
-
- return [result, log];
-}
diff --git a/src/xmutil-js/package.json b/src/xmutil-js/package.json
deleted file mode 100644
index 108aabf6..00000000
--- a/src/xmutil-js/package.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "name": "@system-dynamics/xmutil",
- "version": "1.1.3",
- "description": "Convert Vensim mdl files to XMILE",
- "repository": "http://github.com/bpowers/model-app",
- "author": {
- "name": "Bobby Powers",
- "email": "bobbypowers@gmail.com"
- },
- "license": "MIT",
- "main": "lib",
- "browser": "lib.browser",
- "sideEffects": false,
- "dependencies": {},
- "devDependencies": {
- "@typescript-eslint/eslint-plugin": "^8.18.1",
- "@typescript-eslint/parser": "^8.18.1",
- "eslint": "^9.0.0",
- "eslint-config-prettier": "^9.0.0",
- "eslint-plugin-import": "^2.20.1",
- "eslint-plugin-prettier": "^5.0.0",
- "prettier": "^3.0.0",
- "typescript": "^5.7.2"
- },
- "scripts": {
- "needs-format": "prettier -l '*.ts'",
- "format": "prettier --write '*.ts'",
- "lint": "eslint .",
- "clean": "rm -rf ./lib ./lib.browser",
- "prepublishOnly": "yarn build",
- "build": "yarn clean && tsc && tsc -p tsconfig.browser.json && cp xmutil.wasm* lib/ && cp xmutil.wasm* lib.browser/ && mv lib/index_main.js lib/index.js && mv lib/index_main.js.map lib/index.js.map && mv lib/index_main.d.ts lib/index.d.ts"
- }
-}
diff --git a/src/xmutil-js/tsconfig.browser.json b/src/xmutil-js/tsconfig.browser.json
deleted file mode 100644
index cddc4db7..00000000
--- a/src/xmutil-js/tsconfig.browser.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "extends": "../../tsconfig.base.json",
- "compilerOptions": {
- "rootDir": ".",
- "outDir": "lib.browser",
- "module": "esnext",
- "lib": [
- "es2020",
- "dom",
- "dom.iterable",
- "scripthost"
- ]
- },
- "exclude": [
- "lib",
- "lib.browser",
- "eslint.config.js",
- "index_main.ts"
- ]
-}
diff --git a/src/xmutil-js/tsconfig.json b/src/xmutil-js/tsconfig.json
deleted file mode 100644
index e6180229..00000000
--- a/src/xmutil-js/tsconfig.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "extends": "../../tsconfig.base.json",
- "compilerOptions": {
- "rootDir": ".",
- "outDir": "lib",
- "module": "commonjs",
- "lib": [
- "ES2020",
- "dom"
- ]
- },
- "exclude": [
- "lib",
- "lib.browser",
- "eslint.config.js"
- ]
-}
diff --git a/src/xmutil-js/xmutil.wasm b/src/xmutil-js/xmutil.wasm
deleted file mode 100644
index ccdc0785..00000000
Binary files a/src/xmutil-js/xmutil.wasm and /dev/null differ
diff --git a/src/xmutil-js/xmutil.wasm.d.ts b/src/xmutil-js/xmutil.wasm.d.ts
deleted file mode 100644
index 72cc4944..00000000
--- a/src/xmutil-js/xmutil.wasm.d.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-/* tslint:disable */
-/* eslint-disable */
-export const memory: WebAssembly.Memory;
-export function free(ptr: number): void;
-export function malloc(size: number): number;
-export function xmutil_convert_mdl_to_xmile(
- mdlSourcePtr: number,
- mdlSourcelen: number,
- fileNamePtr: number,
- isCompact: boolean,
- isLongName: boolean,
- isAsSectors: boolean,
-): number;
-export function xmutil_get_log(): number;
-export function xmutil_clear_log();
diff --git a/tsconfig.json b/tsconfig.json
index 081f70a4..574f3d39 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,7 +6,6 @@
{ "path": "./src/app" },
{ "path": "./src/server" },
{ "path": "./src/engine2" },
- { "path": "./src/xmutil-js" },
{ "path": "./website" }
]
}
\ No newline at end of file
diff --git a/website/rspress.config.ts b/website/rspress.config.ts
index 84bb40c7..db398c56 100644
--- a/website/rspress.config.ts
+++ b/website/rspress.config.ts
@@ -75,7 +75,6 @@ export default defineConfig({
'@system-dynamics/core': path.resolve(__dirname, '../src/core'),
'@system-dynamics/diagram': path.resolve(__dirname, '../src/diagram'),
'@system-dynamics/engine2': path.resolve(__dirname, '../src/engine2'),
- '@system-dynamics/xmutil': path.resolve(__dirname, '../src/xmutil-js'),
'@': path.resolve(__dirname, 'src'),
},
},
diff --git a/website/tsconfig.json b/website/tsconfig.json
index d2b410e9..ef4bb128 100644
--- a/website/tsconfig.json
+++ b/website/tsconfig.json
@@ -16,8 +16,7 @@
"@/*": ["src/*"],
"@system-dynamics/core": ["../src/core"],
"@system-dynamics/diagram": ["../src/diagram"],
- "@system-dynamics/engine2": ["../src/engine2"],
- "@system-dynamics/xmutil": ["../src/xmutil-js"]
+ "@system-dynamics/engine2": ["../src/engine2"]
}
},
"include": ["src/", "docs/", "rspress.config.ts"],