Allow @converse/headless to run in NodeJS - #4135
Open
jcbrand wants to merge 4 commits into
Open
Conversation
jcbrand
force-pushed
the
jcbrand/headless-node
branch
4 times, most recently
from
August 7, 2026 02:40
0baa7cc to
c6bf1f8
Compare
jcbrand
force-pushed
the
jcbrand/headless-node
branch
2 times, most recently
from
August 7, 2026 03:01
334098c to
f689758
Compare
One NodeSQLiteStorage instance backs every store (as one localForage does in the browser), so a login opens a single database instead of ~40 files; keys can't collide because getItemName() prefixes each with its store name. It lives at $XDG_STATE_HOME/converse, overridable via storage_path, and needs skeletor 3.1.6. Declaring globalThis.sessionStorage = undefined silences skeletor's probe, which otherwise prints a stack trace on every start and would corrupt a TUI's render.
getCrypto() imported ./libomemo.esm.min.js, a dist/-only sibling, so under Node
it raised ERR_MODULE_NOT_FOUND and the floated rejection killed the process. It
takes a pluggable loader now: the browser fetches the companion file, Node does
`() => import('libomemo.js')`, whose SINGLE_FILE build inlines the wasm, and a
no-op catch keeps a floated failure to "no OMEMO" rather than fatal.
3.0.0 ships no separate curve25519_compiled.wasm, so the rspack builds copy only
the wasm-inlined libomemo.esm.min.js, and the browser loader drops the now-unread
__WASM_BASE__ hint it used to set for the old separate-wasm fetch.
…moji run under Node converse-bosh reports itself disabled off-browser (it's an XMLHttpRequest transport). File upload (XEP-0363) PUTs via fetch under Node, keeping XHR in the browser for upload progress. decodeHTMLEntities and unescapeHTML manage without a DOM, so they no longer throw or return undefined under Node: entities are decoded with `he`, and tags are stripped by a scanner that reads markup the way an HTML tokeniser does. A tag-shaped regex would have been shorter, but it needs the closing `>` and so leaves a truncated `<script src="x` intact, where the DOMPurify call it replaces dropped it. dompurify moves out of headless (the UI still declares it), `he` is added, and the unused DOM-only stringToElement util is dropped from `u`. The microblog plugin and shared/http-upload switch their stray real-`sizzle` imports to `#sizzle`; utils/following.js also loses a stray NUL byte in a local dedup key that had left the file classified as binary. The emoji plugin fetched emoji.json from the asset directory, which has no URL under Node; a pluggable loader reads it from the package instead (via shims/node-emoji.js) while the browser keeps fetching the companion file. The HTML helpers get a spec of their own, which the headless-node project runs alongside the browser one, since the whole point of them is that they're isomorphic.
jcbrand
force-pushed
the
jcbrand/headless-node
branch
from
August 7, 2026 03:33
f689758 to
157354d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.