• exec/tests/msgbase/get_all_msg_headers_to_ext.js skipifsrc/sbbs3/js_ms

    From Rob Swindell (on Windows 11)@VERT to Git commit to main/sbbs/master on Thu May 14 01:58:37 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/ca448cb8bb1a2cb8177f9643
    Added Files:
    exec/tests/msgbase/get_all_msg_headers_to_ext.js skipif
    Modified Files:
    src/sbbs3/js_msgbase.cpp
    Log Message:
    js_msgbase: fix get_all_msg_headers() returning undefined for *_NULL fields

    MsgBase.get_all_msg_headers() returned header objects whose LAZY_STRING_TRUNCSP_NULL-defaulted fields (to_ext, from_ext, replyto, replyto_ext, replyto_list, to_list, cc_list, summary, tags, from_org,
    etc.) yielded `undefined` on first JS access Ä even when the underlying p->msg.<field> was populated. Touching ANY other property first (e.g.
    h.number, h.attr, or JSON.stringify(h) which enumerates) then "primed"
    the object's SpiderMonkey shape and made all subsequent lazy resolves
    work normally on the same object.

    Stock callers (hotline.js, msglist.js, msgutil.js, etc.) didn't trip on
    this because they all happen to access a non-NULL field (number, attr, when_imported, ...) before any *_NULL field, masking the bug. It only
    surfaces in code that reads a *_NULL field as the first property
    touched on a bulk-fetched header Ä which is exactly what filtering by
    to_ext or from_ext naturally does.

    get_msg_header() does not exhibit this because each retrieval is
    followed by user code that organically touches a non-NULL field, again
    priming the shape before any *_NULL access.

    Fix: in js_get_all_msg_headers, eagerly JS_DefineProperty("number",
    ...) immediately after JS_SetPrivate on each fresh header object. That
    single defineProperty triggers the SpiderMonkey shape transition once
    per header at construction time, so the first lazy resolve of a *_NULL-defaulted field operates on a settled shape and returns the
    correct value.

    Add a regression test in exec/tests/msgbase/:

    - get_all_msg_headers_to_ext.js Ä creates a temp msgbase, saves a
    message with to_ext="1", reopens, fetches all headers, and reads
    h.to_ext as the FIRST property touched on each bulk-fetched header.
    Throws if the value is anything other than "1".

    - skipif Ä skips the entire msgbase/ test category when MsgBase isn't
    available (e.g. under JSDoor).

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net