Add a new diagram covering the authentication mechanism: a
session-based /SASLogon/login + /SASLogon/authorize handshake that
mints a short-lived auth code, exchanged at /SASjsApi/auth/token for a
revocable JWT pair, with optional LDAP-backed credential verification.
Add WHY comments (not present before) at the handful of spots in the
auth code whose behaviour isn't obvious from reading them in isolation:
why /auth/token can return an existing token pair instead of rotating
it, why the auth-code exchange never checks a client's clientSecret,
how verifyTokenInDB turns self-verifying JWTs into revocable ones, why
two /SASjsApi/user routes stay reachable in desktop mode, why the
static authorized-route list exists on top of plain authentication,
and why /SASjsApi/client's real protection lives at its router mount
point in routes/api/index.ts rather than in client.ts itself.
For JS/PY/R, processProgram sets session.state = failed (with
failureReason) itself when the interpreter process exits non-zero,
without throwing. ExecutionController.executeProgram then
unconditionally set session.state = completed right after
processProgram returned, silently overwriting that - so anything
downstream inspecting session.state (e.g. scheduleSessionDestroy's
expiresAfterMins branch) would see a crashed session mis-reported as
successful.
Guard the assignment so a failed state is never overwritten. No
effect on SAS, which sets state independently via its own spawned
process lifecycle.
Added Execution.spec.ts covering both the failure case (state stays
failed) and the success case (state still becomes completed), to
guard against regressing in either direction.
The SAS-runtime poll loop in processProgram only checked for
SessionState.completed, so a session that failed (e.g. via %abort;)
left the loop spinning on delay(50) forever - the request never
resolved, even though the SAS log had already been written. Split a single completed-on-either-outcome
flag into separate completed/failed states without updating this loop.
- processProgram now throws when session.state becomes 'failed'
- Execution.ts catches that, reads the complete log (guaranteed
complete since the session's process has already exited by then),
and throws a SessionExecutionError carrying it
- stp.ts/code.ts surface { ..., log } in the HTTP error response
- add unit tests for the poll loop and the log-enrichment logic, plus
a mock SAS executable + end-to-end test exercising the real
session/process pipeline without needing a SAS install
- add docs/diagrams covering the session lifecycle and the SAS
execution handshake mechanism, for future context