JetCore engine (Tag.cpp/.h, Global.cpp/.h, __*.cpp tag classes):
- Numerous __mysql.cpp connection-pooling correctness fixes (stale
result sets, double-indirection sessionid bugs, field caching,
pool keying) and a session-registration-before-connection-confirmed
ordering fix (dangling pointer on a failed connect)
- Tag.cpp: large body of fixes to the tag-dispatch/container-scanning
logic (bare-name fallback, database aliasing, single-quote handling,
@-prefix session variable resolution, TAGDEF caching, nested split-tag
scanning for if/while/until/ifrow so an inner one doesn't corrupt an
outer one's own boundary scan) plus the process-first rendering
order fix (a <process> now runs before <checksession>/page
resolution, fixing a real "blank page after successful login" bug)
- Global.h/.cpp: added imapSessions (parallel to the existing __mysql*
session map) for the new <imap> tag family
- __whiledir.cpp/__include.cpp: caching and directory-name-aliasing
fixes
- __call.cpp/.h: fixed a dangling-pointer bug
- __write.cpp: fixed an O_WRONLY-only open that couldn't create a new
file
- __ifrow.cpp/__sql.cpp: fixed the same sessionid double-indirection
bug __mysql.cpp had (resolveKeyword's result was being used as a map
key lookup instead of the resolved value itself)
jetc compiler (jetc/JetParser.cpp/.h, JetCodegen.cpp/.h, JetAST.h,
jetc.cpp, build-shared.sh, build-compiled.sh):
- Parser fixes: inline <include>, nested-quote handling, void-tag
recognition
- Codegen fixes and expanded AST support
- build-shared.sh: atomic rename into place for a freshly-built .jtc
(a live server dlopen()ing the same path mid-rebuild was corrupting
its own already-mapped view of the old file) and a staleness check
so a cached JetCore .o doesn't silently keep serving yesterday's
fixes forever
- Added -lssl to both build scripts' link lines (needed once the new
<imap> tag's client-side TLS code existed - previously nothing in
this codebase used OpenSSL's SSL/TLS client API, only libcrypto)
Site content (jetc/barant-site/): SPA (fetch-and-swap navigation)
rollout across essentially every .page/.jet/.section/.tag/.proc/.method
file - dynamic-content fixes, fragment-mode stripping, native
$[@name] session variables replacing older mechanisms, and this
session's own checkout.page redesign, login start-param redirect fix,
and per-page side-menu (menu.bar/$[PAGE].menu) refresh support.
Completed the earlier main.css/control.css move from assets/ to
assets/images/ (git still showed the old paths as tracked-but-deleted
until now).
Deliberately left out of this commit: dozens of .pre-<description>-
<date> manual backup files (this session's and earlier ones' own
before/after snapshots, not meant for permanent history), compiled
.jtc build outputs, ad hoc test binaries and scripts under jetc/
(login_asan, wcwc_test, optest2_bin, etc.), and jetc/__pycache__/ -
none of these are meant to be tracked; a .gitignore covering them
would be worth adding separately.
Parsed its arguments correctly but had an empty body (a literal
"// TODO: Implement DATEDIFF." comment), never setting a result - isNumber
was left uninitialized on this path.
Matches MySQL's own DATEDIFF(expr1, expr2): expr1 - expr2 expressed as a
whole number of days, ignoring time-of-day. Dates parse as YYYY-MM-DD via
strptime (a longer "YYYY-MM-DD HH:MM:SS" value parses fine too, since
strptime only consumes what the format string asks for) and are
interpreted as UTC via timegm() rather than mktime(), so the result
doesn't shift with the server's own timezone. An unparseable date throws
a clear, specific error instead of producing garbage.
Verified via real jet-2.0 runs: datediff('2026-08-26','2026-08-20') -> 6,
the reverse -> -6, same date -> 0, a full non-leap year -> 365, and an
invalid date string throws cleanly rather than crashing or silently
returning nonsense. Full 9-script regression suite still passes.
Both matched their function name but had empty branch bodies, silently
producing nothing regardless of arguments. reverse(str) follows the same
single-argument shape as trim/toupper/tolower; concat(a, b, ...) follows
the same variadic comma-separated shape as max/min.
reverse() converts to std::string and reverses in place via the already-
imported <algorithm> (std::reverse), then assigns back through MString's
std::string constructor. concat() starts from the first argument and
appends each subsequent one via MString's own operator<<(ZString).
Verified via real jet-2.0 runs: reverse('hello') -> "olleh",
concat('a','b','c') -> "abc", concat('x','y') -> "xy". Full 9-script
regression suite (custom tag scoping, dump tag tree, try/catch, if/else,
keyword sigil) still passes unchanged.
Found while writing a new Expressions Reference chapter for the manual,
where every function got run for real before being documented rather
than trusted from reading the source alone - substring('helloworld', 7)
threw "operand is not valid." immediately after producing the correct
answer, confirmed via debug tracing showing an extra Operand constructed
on empty input right after the 2-arg case finished.
Root cause: a missing pair of braces around the 3-argument-only branch
meant Operand parm3(in, tag) and its own closing-paren check ran
unconditionally, even after the 2-arg form's own "if(in.ifNext(')'))"
check had already matched and produced string = parm1.string.substring(
parm2.string.asInteger()) - so it immediately tried to parse a third
argument from whatever was left (nothing, since the real closing paren
was already consumed), and construction on empty input always throws.
The 3-argument form (substring(str, start, length)) was unaffected,
since its own path never hit the first if-branch's early exit.
Fixed by properly scoping the 3-arg branch inside an else, so it only
runs when the 2-arg form's own closing paren wasn't found. Verified both
forms with real jet-2.0 runs: substring('helloworld', 7) -> "rld",
substring('helloworld', 0, 5) -> "hello".
When resolving a bare $[var]/#[var] reference, Operand set .boolean =
true whenever the value's text was EITHER "true" OR "false" -
effectively "boolean = looks-boolean-ish" rather than reflecting which
of the two it actually was. <if expr="..">/<while expr="..">check
exactly this .boolean field (see __if.cpp/__while.cpp), so <if
expr="$[flag]"> took the true branch even when flag held the literal
text "false".
Fixed to boolean = (string == "true").
Verified live: a variable set to value="false" now correctly takes an
<if expr="$[var]">'s false branch, and value="true" still correctly
takes the true branch. Re-ran tests/posttest.sh - unaffected, no
regression.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>