Skip to content

v4.0.4

Latest

Choose a tag to compare

@github-actions github-actions released this 15 Aug 19:07
· 1 commit to main since this release

Added

  • HTTPS dev/preview server support - dev.https / preview.https (previously declared but ignored) now serves the dev/preview server over HTTPS. https: true generates a self-signed dev certificate; https: { cert, key } uses real certs (PEM content or file paths). CLI flags --https, --cert <path>, --key <path> added to dev and preview. The WebSocket server stays attached to the same HTTPS server, so WSS works on the same origin.
    • @elitjs/https Server now proxies the upgrade event (previously only error/close), enabling WebSocket-over-HTTPS on Node and fixing the standalone @elitjs/wss package.
    • WebSocket-over-HTTPS also works on Bun (@elitjs/https Bun branch now mirrors @elitjs/http's WebSocket plumbing). Deno still has no WebSocket support at all, not only over HTTPS — tracked as a separate effort.
    • Self-signed dev certs via selfsigned@2 (sync API, keeps createDevServer synchronous and avoids a breaking public-API change).

Changed

  • Preview mode minifies browser modules with esbuild instead of obfuscating them - elit preview previously ran transpiled .ts/.tsx modules through javascript-obfuscator (string-array obfuscation); preview output is now minified (identifiers + whitespace) by the same esbuild transform that transpiles the module. Removes the javascript-obfuscator dependency (~11.9 MB unpacked, 20 transitive deps) from @elitjs/server, @elitjs/dev-build, @elitjs/preview-build, and the root elit package. Trade-off: minification mangles code structure but, unlike string-array obfuscation, leaves string literals readable. Dev mode is unchanged.
  • Client package browser exports now point at the ES module build - @elitjs/el, @elitjs/dom, @elitjs/style, @elitjs/state, @elitjs/router, and @elitjs/render-context mapped their browser condition to dist/index.iife.js, which has no exports; every bundler build with browser platform resolution got undefined for all named imports (e.g. import { div } from '@elitjs/el'). The browser condition now resolves to dist/index.mjs.

Fixed

  • Client WebSocket behind TLS-terminating proxies (e.g. Cloudflare) - the @elitjs/hmr client and the dev-server-injected HMR script derived the WebSocket URL from the dev server's host:port instead of the page origin, so behind a proxy the public origin differs from the server's port and every wss:// connection failed. Both now use window.location.host (same origin); the hmr client also targets the /__elit_ws endpoint it previously omitted.
  • SharedState reconnect spam - it retried every 1s forever and logged each failure. Now uses capped exponential backoff (1s→30s, max 10 attempts), stays quiet per attempt, warns once when giving up, and stops reconnecting after destroy() even if a close event arrives late.
  • reactive() sometimes didn't update - the DOM renderer fires ref asynchronously (via setTimeout), so the first state change could land before reactive()'s element reference was set; the update was silently dropped and the element stayed on its initial snapshot. reactive() and reactiveAs() now track the missed update and replay it once the element exists. Also, scheduleRAFUpdate falls back to setTimeout when the tab is hidden, so state-driven updates keep flowing in background tabs where requestAnimationFrame is paused.
  • @elitjs/server bundled a broken inlined copy of esbuild - the server's tsup config did not mark esbuild as external, so the runtime import('esbuild') in transpileNodeBrowserModule resolved to an inlined chunk that fails to load ("Dynamic require of fs is not supported"), silently disabling every esbuild-backed transpile path (.tsx files, the .ts esbuild fallback, and now preview minification). esbuild is now external and resolved from node_modules at runtime, matching @elitjs/dev-build and @elitjs/preview-build.
  • elit test could not load test files importing Node builtins or packages - the test runner's nodeRequire probe (typeof require !== 'undefined') was rewritten by esbuild's require shim into a tautology, so it always used the throwing shim instead of createRequire(import.meta.url). The same pattern in @elitjs/ws, @elitjs/chokidar, @elitjs/fs, @elitjs/http, and @elitjs/https crashed those packages whenever the test runner evaluated their source (createRequire(undefined)). All sites now try __filename first (present when the runner evals transpiled CJS) and fall back to import.meta.url (real ESM builds).
  • elit test leaked mocked browser globals across test files - example tests that mock document, fetch, window, EventSource, timers, etc. at module scope left the mocks installed for every later file in the same process, which broke tsup's import.meta.url CJS shim ("Invalid URL" loading cli.test.ts), broke dom.test.ts with a partial document, and hung the e2e-* suites on a never-firing mocked setTimeout. The runner now snapshots these globals before the first test file and restores them before every subsequent file.
  • Lifecycle hooks were file-global instead of suite-scoped - beforeEach/afterEach/beforeAll/afterAll registered inside one describe ran for every test in the file, so hooks from sibling describes overwrote each other's global mocks (e.g. state.test.ts's WebSocket mock being replaced before its assertions ran). Hooks now attach to the suite where they are declared; beforeEach/afterEach compose down the suite ancestry (outer→inner / inner→outer) and beforeAll/afterAll run once per suite.
  • PM record writes crashed the process - writePmRecord wrote apps/<id>.json without creating its directory, so the process-manager runner crashed with ENOENT when the record directory did not exist yet. It now creates the directory recursively.
  • E2E request interception could kill the test host - Fetch.requestPaused dispatch discarded the handlePausedRequest promise, so a paused request that failed to resume (mocked session, command timeout) escaped as an unhandled rejection and crashed the whole elit test process before the summary printed. The dispatch now catches and reports these failures.
  • PM proxy blocked its own managed instances - the SSRF guard rejected loopback targets (127.0.0.1, ::1), but the PM proxy's primary job is forwarding to the local instances it spawns. Loopback is now allowed while all other private/reserved ranges stay blocked.
  • Stale tests after the refactors - database.test.ts now targets packages/function-store (renamed from packages/database); html-entrypoint.test.ts points at the restructured create-elit templates (basic-example); dom.test.ts reads the expected elit-version from packages/dom/package.json instead of a hardcoded version; desktop-cli.test.ts compares resolved paths via path.resolve instead of hardcoded POSIX strings (the functions intentionally return native separators); and the full-db client test expects the current /main.js output layout.

Full Changelog: v4.0.3...v4.0.4