WebAssembly and JavaScript ========================== The web integration runs the existing C++ DIPL parser and inspector, and the PUQ conversion command, in a browser through WebAssembly. Its JavaScript module provides string and bundled-project parsing, value descriptions, schema inspection, validation, override reevaluation, and unit conversion. It has no browser UI dependency. The JavaScript module is built from this repository and is not currently published as an npm package. Build it with Emscripten's ``emcmake`` and a C++17-capable toolchain: .. code-block:: shell export EM_CACHE="$PWD/build-web/cache" emcmake cmake -S bindings/web -B build-web -G Ninja cmake --build build-web -j4 node bindings/web/tests/smoke.mjs Emscripten also requires Python 3.10 or newer. Put that ``python3`` first on ``PATH`` if your operating system provides an older default. ``EM_CACHE`` keeps the compiler cache in a writable project build directory. The build writes ``bindings/web/dist/dipl_web.mjs`` and ``bindings/web/dist/dipl_web.wasm``. When serving these files to a browser, serve the JavaScript and WASM from the same origin and use a server that sends ``application/wasm`` for the ``.wasm`` file. The module initializes asynchronously. Evaluated values are returned as DIPL text, including units separately, so callers do not lose integer precision or array type information through an implicit JavaScript conversion: .. code-block:: javascript import { loadDIPL } from "./bindings/web/index.mjs"; const dipl = await loadDIPL(); const source = `distance float = 24 m duration float = 8 s speed float = ({?distance} / {?duration}) m/s`; const model = dipl.parse(source); console.log(model.paths()); console.log(model.describe("speed").valueText); console.log(model.describeMany(["distance", "duration", "speed"])); const candidate = model.withOverride("distance = 32 m"); console.log(candidate.describe("speed").valueText); console.log(dipl.validate(source, "missing = 1")); candidate.dispose(); model.dispose(); ``withOverride`` evaluates the original source with a replacement override body and returns a new model. It does not mutate the original. This uses the same parser and validation rules as native DIPL. ``schemas()`` exposes the schema definition and application hierarchy for building navigation or forms. ``describeMany(paths, maxValueElements)`` returns one description per requested path, in the same order, through a single WebAssembly call. It uses the model's already evaluated environment; each result has the same fields and value limits as ``describe()``. Pass the paths you need to avoid serializing the full model. See :doc:`../modules/dip/inspection` for the related C++ inspection API. For a model split across several sources, pass text-backed registrations to ``parseProject``. This calls the core C++ project registration method, which processes units, named schema bodies, existing overrides, and ordered code in the same phases as a DIPfile. Code and override ``name`` fields are source identities used in diagnostics; schema and unit ``name`` fields are their DIPL names. Schema bodies and override bodies have no ``$schema`` or ``$override`` wrapper. A website can fetch or generate this object without providing a filesystem to the parser: .. code-block:: javascript const project = { units: [{ name: "custom_length", text: "m" }], schemas: [{ name: "settings", text: "distance float = 24 custom_length\nduration float = 8 s" }], code: [ { name: "model.dip", text: "simulation : settings" }, { name: "derived.dip", text: "speed float = ({?simulation.distance} / {?simulation.duration}) m/s" }, ], overrides: [], }; const model = dipl.parseProject(project); const next = model.withOverride("simulation.distance = 32 custom_length"); console.log(next.describe("speed").valueText); console.log(dipl.validateProject(project, "unknown = 1")); next.dispose(); model.dispose(); ``withOverride`` replaces the previous interactive override body and leaves ``project.overrides`` intact. DIPL rejects two overrides of the same path. If the project input already contains an override for the path being edited, replace that entry in ``project.overrides`` and call ``parseProject`` again; the web binding retains the core parser's duplicate-override rule. ``parseProject`` remains useful for projects assembled in memory. For a DIPfile with local files, package it with the SNT CLI instead. The package command uses the native C++ project loader to collect files opened during evaluation, including ``sources[]`` entries and raw ``.dipt`` tables. It rejects missing files and paths outside the DIPfile directory. .. code-block:: shell snt dip package --project path/to/DIPfile --output project.bundle.json The result is a static JSON object containing ``format``, ``entry`` and ``files``. Its paths are relative to the DIPfile directory. A site may fetch this asset explicitly, then load it without further network access: .. code-block:: javascript const bundle = await fetch("./project.bundle.json").then((response) => response.json()); const model = dipl.parseBundle(bundle); console.log(model.describe("measurements.mass")); const candidate = model.withOverride("scale = 4"); console.log(candidate.describe("result").valueText); console.log(dipl.validateBundle(bundle, "scale = 4")); candidate.dispose(); model.dispose(); The browser calls the same C++ DIPfile registration and table parsers as native SNT. A missing bundled file or a path escaping the bundle is an error; the runtime does not fetch it. ``$source`` paths derived from values are collected when packaging the original project. If a later override selects a different file, pass ``--include relative/path`` to ``snt dip package`` or the override will fail clearly. Run the package command from the project directory when its ``$source`` paths are relative to the working directory. DIPH5 persistence, reports, adapters, and DIPL source serialization are separate features. PUQ conversion -------------- ``loadPUQ`` exposes the existing C++ ``PUQConvert`` command to JavaScript. It accepts the same expression, target units, and optional unit-system and physical-quantity context as ``snt puq convert``: .. code-block:: javascript import { loadPUQ } from "./bindings/web/index.mjs"; const puq = await loadPUQ(); console.log(puq.convert("35*eV", "J")); console.log(puq.convert("12*statA", "A", { inputSystem: "ESU", outputSystem: "SI", outputQuantity: "I", })); console.log(puq.systems()); The :doc:`PUQ conversion page <../modules/puq/conversion>` includes a live browser converter using this interface. The documentation build compiles the WebAssembly module and copies it into Sphinx's static assets.