Skip to content

Publish numeric on the global object so browsers can use it - #86

Merged
dqnykamp merged 2 commits into
Doenet:2.xfrom
dqnykamp:fix-numeric-global
Aug 7, 2026
Merged

Publish numeric on the global object so browsers can use it#86
dqnykamp merged 2 commits into
Doenet:2.xfrom
dqnykamp:fix-numeric-global

Conversation

@dqnykamp

@dqnykamp dqnykamp commented Aug 7, 2026

Copy link
Copy Markdown
Member

me.math.dopri throws ReferenceError: numeric is not defined in a browser and in a web worker, while working fine under Node. DoenetML's <odeSystem> integrates with it, so every activity containing an ODE currently dies on load — the worker error surfaces as a red "numeric is not defined" banner in place of the document.

Why

numeric.js builds most of its helpers at load time with the Function constructor. The generated bodies reference a bare numeric:

if(typeof _s === "undefined") _s = numeric.dim(x);

Functions made with Function(...) are evaluated in global scope, so that reference resolves only if numeric is a property of the global object. numeric.js puts it there itself — but through a Node-ism:

if (typeof global !== "undefined") { global.numeric = numeric; }

A browser main thread and a web worker both have no global, so the assignment is skipped and every generated helper (add, mul, sub, dim, …) throws the first time it is called. dopri reaches those helpers immediately.

The change

lib/mathjs.js — the one place that imports numeric — publishes it itself, which covers every runtime.

The assignment is unconditional rather than guarded on globalThis.numeric === undefined. On a page holding an element whose id is numeric, the named-element global makes the slot look occupied while still being useless to the generated code — which is exactly what happens in DoenetML, whose virtual keyboard has a numeric button, and it turns the error into the more puzzling dim is not a function.

Test

spec/quick_numeric-global.spec.js deletes global before the first import of lib/mathjs.js, reproducing the shape a browser sees, then integrates x' = x from x(0) = 1 and checks it lands on e. Without the fix it fails with the production error:

ReferenceError: numeric is not defined
 ❯ eval node_modules/numeric/numeric-1.2.6.js:696:26
 ❯ Object.dopri node_modules/numeric/numeric-1.2.6.js:2916:49

npm test is green (3580 tests, 19 files). Verified end to end against DoenetML: with the equivalent shim in place, an <odeSystem> document renders its values and plots its solution curve in both the worker and the main-thread renderer.

Base branch

Against 2.x, branched from v2.0.0-alpha94 — the published 2.x line that DoenetML consumes. main no longer contains lib/mathjs.js; the Rust port (#77) removed it. A companion PR, #87, restores numeric in packages/math-expressions-js-compat on main, where me.math currently has no dopri at all.

A second commit bumps the version to 2.0.0-alpha95, so this can be published as soon as it merges.

🤖 Generated with Claude Code

dqnykamp and others added 2 commits August 7, 2026 12:55
numeric.js builds most of its helpers at load time with the `Function`
constructor, and the generated bodies reference a bare `numeric` — for
instance `if(typeof _s === "undefined") _s = numeric.dim(x);`. Functions
made that way are evaluated in global scope, so the reference resolves
only if `numeric` is a property of the global object.

numeric.js puts it there itself, but through a Node-ism:

    if (typeof global !== "undefined") { global.numeric = numeric; }

A browser main thread and a web worker both have no `global`, so the
assignment is skipped and every generated helper — `add`, `mul`, `sub`,
`dim`, … — throws `ReferenceError: numeric is not defined` the first
time it is called. `math.dopri` reaches those helpers immediately, so
integrating an ODE fails outright in the browser while passing in Node.

Publish `numeric` from here instead, which covers every runtime.

The assignment is unconditional rather than guarded on
`globalThis.numeric === undefined`: on a page holding an element whose id
is `numeric`, the named-element global makes the slot look occupied while
still being useless to the generated code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dqnykamp
dqnykamp merged commit 50b74e4 into Doenet:2.x Aug 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant