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"],