ReviewOS

stacks/bun-query-builder

Harden config singleton to remove brittle build.ts regex-patching (globalThis/Symbol)

#1043
Closed glennmichael123 opened this 2 months ago ·
2 months ago

Summary

build.ts rewrites Bun's generated output with regexes (var (init_src\d*) = __esm(...), init_config(); → await init_config();) to keep setConfig writing the same config binding readers use. The in-file comments document that this already silently broke once (a config3 → config5 rename made an earlier patch a no-op). Any Bun bundler change to wrapper naming / __esm shape breaks setConfig silently again, with no test guarding it.

Suggested fix

Store config on a globalThis[Symbol.for('bun-query-builder/config')] ??= { ...defaultConfig } singleton and export const config = .... That removes both the export let split-binding hazard and the entire build.ts patch (no module-local binding for the bundler to rename/wrap). Pitfalls to note: a Symbol.for singleton is shared across all package versions in a process (dedup risk if two majors coexist), and it doesn't fix the documented multi-dialect cross-contamination — but it's strictly less fragile than patching emitted JS. At minimum, add a post-build assertion that setConfig({dialect:'sqlite'}) actually flips a reader.

Severity: medium (robustness). Relates to #1022. Found via internal code audit.

8 days ago

Fixed in 014a86d. config is now export const config = (globalThis[Symbol.for('bun-query-builder.config')] ??= { ...defaultConfig }) — a process-wide singleton every inlined copy shares, so a bundler binding-split can no longer desync setConfig from the model-layer readers, and const removes the 'never reassign config' foot-gun. Synchronous (no top-level await) so bun --compile is unaffected.

Verified by building the dist and confirming setConfig flips config.dialect AND routes a model write to the configured connection; added a dist-gated guard test that imports the BUILT bundle in a fresh process (catches bundler splits a source test can't).

Note: build.ts's init_config async patch is retained — it handles the bundler's __esm async-wrapping ordering, which is orthogonal to the binding-split hazard this addresses. With the singleton, that patch is now defense-in-depth rather than load-bearing for config sharing.

Sign in to comment on this issue.