Skip to content

fix: make module importable in DOM-free environments (Node/SSR)#39

Open
dmchaledev wants to merge 1 commit into
mainfrom
claude/sleepy-rubin-7ormpc
Open

fix: make module importable in DOM-free environments (Node/SSR)#39
dmchaledev wants to merge 1 commit into
mainfrom
claude/sleepy-rubin-7ormpc

Conversation

@dmchaledev

Copy link
Copy Markdown
Contributor

Problem

The package advertises a pure, DOM-free calculator and Next.js support, but the module can't actually be imported outside a browser.

  • README.md: "Or import the pure DOM-free calculator: import { calculate } from '@hailbytes/vulnerability-calculator';" and lists Next.js as a supported host.
  • index.d.ts: calculate() is documented as "Pure infrastructure-sizing calculation — no DOM required."

Yet three top-level statements execute DOM references at module-load time:

Location Reference
class HailbytesVulnCalculator extends HTMLElement HTMLElement (superclass)
const TMPL = document.createElement('template') document
customElements.define(...) customElements

So a plain Node / SSR / Next.js server component import throws immediately:

ReferenceError: document is not defined
    at hailbytes-vuln-calculator.js:553

The existing tests don't catch this because they inject a full DOM shim into globalThis before importing the module — so the real off-DOM path was never exercised.

Fix

Guard the three DOM touchpoints so the module loads without a browser, leaving the in-browser web-component path byte-for-byte unchanged:

  • Derive the template markup into a plain string; only build the <template> element when document exists.
  • Extend a plain base class when HTMLElement is undefined, so the class declaration itself doesn't throw off-DOM. (The constructor's DOM calls are only reached when the browser instantiates the element, which never happens off-DOM.)
  • Call customElements.define() only when customElements exists.

Verification

Direct import now works with no shim:

$ node --input-type=module -e "import { calculate } from './hailbytes-vuln-calculator.js'; \
  console.log(calculate({target_hosts:2000,scan_intensity:'medium',scan_frequency:'weekly', \
  scanning_tools:['hailbytes_asm'],compliance_needs:['pci']}).vm_resources.cpu_cores)"
12

Adds test/dom-free.test.mjs as a regression guard — it imports the module without any DOM shim (node --test runs each test file in its own process) and calls calculate(). Full suite is green:

# tests 67
# pass 67
# fail 0

Scope

One source file + one new test. No API, output, or browser-behavior changes; purely makes the already-documented off-DOM import contract actually hold.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CBGKeWziEpyQeGBiD1LiKv


Generated by Claude Code

The package advertises a "pure DOM-free calculator" (`import { calculate }`)
and index.d.ts documents calculate() as "no DOM required", plus the README
claims Next.js support. But the module referenced HTMLElement (superclass),
document.createElement, and customElements.define at top level, so importing
it in Node/SSR threw `ReferenceError: document is not defined` at load time.
The existing tests only passed because they inject a full DOM shim into
globalThis before importing.

Guard the three DOM touchpoints so the module loads without a browser:
- derive the template's HTML into a plain string; only build the <template>
  element when `document` exists
- extend a plain base class when `HTMLElement` is undefined so the class
  declaration itself doesn't throw off-DOM
- register the custom element only when `customElements` exists

Add test/dom-free.test.mjs, which imports the module with no DOM shim
(node --test runs each file in its own process) and calls calculate(), as
a regression guard. The web-component path is unchanged in the browser.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CBGKeWziEpyQeGBiD1LiKv
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.

2 participants