@ti-engine/web-framework 1.18.1 → 1.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.env +4 -4
  2. package/CHANGELOG.md +362 -341
  3. package/README.md +73 -40
  4. package/bin/build/post-install.js +18 -18
  5. package/bin/localization/web-server-labels.json +27 -27
  6. package/bin/static/.well-known/appspecific/com.chrome.devtools.json +5 -5
  7. package/bin/static/fragments/components/component-notification-bar.html +21 -21
  8. package/bin/static/fragments/components/component-sidebar-flyout.html +36 -36
  9. package/bin/static/fragments/components/component-sidebar.html +33 -33
  10. package/bin/static/fragments/components/component-tooltip.html +10 -10
  11. package/bin/static/fragments/components/component-topbar.html +5 -5
  12. package/bin/static/fragments/frame-administration.html +2 -2
  13. package/bin/static/fragments/frame-application.html +18 -18
  14. package/bin/static/fragments/frame-dashboard.html +2 -2
  15. package/bin/static/fragments/frame-login.html +119 -119
  16. package/bin/static/fragments/frame-not-found.html +2 -2
  17. package/bin/static/fragments/frame-profile.html +2 -2
  18. package/bin/static/index.html +22 -22
  19. package/bin/static/scripts/ti-charts.js +1591 -1591
  20. package/bin/static/scripts/ti-framework.css +3194 -3194
  21. package/bin/static/scripts/ti-framework.js +1427 -1427
  22. package/bin/static/scripts/ti-theme-black-glass.css +216 -216
  23. package/bin/static/scripts/ti-theme-daylight.css +87 -87
  24. package/bin/web-app-manager.js +663 -663
  25. package/bin/web-server.js +936 -744
  26. package/bin/web-server.json +48 -48
  27. package/components/admin-config-handlers.js +92 -92
  28. package/components/auth-manager.js +441 -441
  29. package/components/authorization.js +135 -135
  30. package/components/config-change-notifier.js +98 -98
  31. package/components/config-registry.js +260 -260
  32. package/components/config-service.js +360 -360
  33. package/components/config-store.js +246 -246
  34. package/components/definitions.types.js +26 -26
  35. package/components/session-store.js +110 -110
  36. package/components/user.js +132 -132
  37. package/components/web-config-env.js +19 -2
  38. package/components/web-handlers.js +800 -800
  39. package/package.json +76 -67
package/CHANGELOG.md CHANGED
@@ -1,341 +1,362 @@
1
- # ti-engine web-framework changelog
2
-
3
- This document will contain the list of changes made to the framework. The format is based on the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.
4
-
5
- ## Version 1.18.1
6
-
7
- Enabling Azure SSO took the instance down at startup: the OAuth2 callback was registered by handing the configured callback value straight to Express as a route path, and the installation docs tell operators to set that value to the full absolute URL registered with the identity provider. Express 5 parses route patterns with path-to-regexp v8, where `:` opens a parameter name so `https://host/login/azure-callback` throws `Missing parameter name at index 6` and the web server never starts. Google was affected identically, which also made this a prerequisite for the competence Cloud Run deployment, whose `deploy.sh` patches in an absolute callback URL (CA-97).
8
-
9
- * fix(web-framework): register an OAuth2 callback by its **path** rather than by the configured value verbatim, so a callback given as the absolute URL registered with the provider no longer crashes startup; a callback that yields no usable path now logs a WARNING and skips that provider's endpoint instead of taking the instance down, matching how an enabled-but-unconfigured provider is already handled
10
- * feat(web-framework): add `AuthManager.getOAuth2CallbackPath( authMethod )` and the pure, unit-tested `AuthManager.toCallbackPath( callbackUrl )` — reduces an absolute, protocol-relative, path or bare relative callback to its route path (query string and fragment stripped), or `null` when no usable path can be derived. The `redirect_uri` sent to the provider is deliberately left as configured, so an absolute callback keeps matching the provider registration exactly instead of depending on the forwarded protocol/host being correct
11
- * docs(web-framework): document the OpenID Connect provider variables in the README, and state in the README, the competence `INSTALL.md` and `.env.example` that a callback URL may be given as either the absolute registered URL or a path — including what each implies for the `redirect_uri`, and that the path must be the one the app actually receives when a proxy strips a prefix
12
- * build(release): bump package version from `1.18.0` to `1.18.1`
13
-
14
- ## Version 1.18.0
15
-
16
- Every web-server setting a container deployment needs could be supplied per environment except one: the admin allowlist. `auth.admins` was readable only from the config file baked into the image, so a containerized deployment had no way to name an administrator leaving the admin configuration screens unreachable, or forcing a real identity to be committed to the repository. This closes that gap in the existing `TI_WEB_*` override set (CA-94).
17
-
18
- * feat(web-framework): add the `TI_WEB_AUTH_ADMINS` environment override comma-separated, **replaces** `auth.admins` (matched against the session user's user ID, username or email), so the admin allowlist is configurable per environment like every other web setting; an explicitly empty value means no admins
19
- * docs(web-framework): document `TI_WEB_AUTH_METHODS` and `TI_WEB_TRUSTED_ORIGINS` in the README's environment-variable list, which had never listed them
20
- * build(release): bump package version from `1.17.1` to `1.18.0`
21
-
22
- ## Version 1.17.1
23
-
24
- A validator that needs to compare its own config document against its previously committed state had no way to do so: `applyEdits`'s cross-document context resolves `getConfig` to the *pending* value for any document inside the current edit batch — by design, so a validator can check a sibling document's post-edit state — but a document is always part of its own edit batch, so calling `getConfig` on itself just hands back the same incoming value already passed as the validator's argument, never its prior state. This silently defeated the competence `research-consent` config's version-bump guard (CA-93).
25
-
26
- * feat(web-framework): add `getStoredConfig(key)` to the `applyEdits` validator context (`ConfigService`) — always resolves the current *committed* value from the store, even for the document currently under validation, so a validator comparing its own document against its previous state has a way to do it; purely additive — `getConfig`'s existing cross-document (pending-value) semantics are unchanged
27
- * build(release): bump package version from `1.17.0` to `1.17.1`
28
-
29
- ## Version 1.17.0
30
-
31
- Route-registration seams so an application subclass can add its own Express routes and unprotected-route patterns enabling public, content-driven sites (the first consumer being the standalone author's site) to layer a catch-all content resolver over the framework without reaching into private state.
32
-
33
- * feat(web-server): add `TiWebServer.registerRoute( method, path, ...handlers )` — registers a custom route on the underlying Express app from a `defineWebApplicationRoutes()` override (after `super()`), so a catch-all resolver can be mounted after the framework's own routes but before its `*splat` 404 handler. Limited to route-scoped verbs (get/post/put/patch/delete/options/head/all); raises `E_GEN_INVALID_ARGUMENT_TYPE` for any other method and `E_GEN_NOT_INITIALIZED` if called before the Express app exists. The method must be a **string** — a non-string is rejected outright rather than coerced, so a value whose `toString()` happens to yield a verb (`[ "get" ]`, `new String( "get" )`) cannot slip past the allowlist and register a route
34
- * feat(web-server): add `TiWebServer.addUnprotectedRoute( pattern )` — appends a string (exact-match) or RegExp (tested) pattern to the unprotected-routes list from a `defineUnprotectedRoutes()` override, so a public-by-default site can invert the framework's protect-by-default stance; non-string/non-RegExp values are ignored with a warning
35
- * refactor(web-server): extract the unprotected-route matching loop from `isUnprotectedRoute()` into the pure, unit-tested `isRouteInList()` helper (behavior unchanged, including the defensive `lastIndex` reset), and add the `normalizeRegistrableMethod()` helper — both exported for testing alongside the existing `RE_*` matcher constants
36
- * build(release): bump package version from `1.16.0` to `1.17.0`
37
-
38
- ## Version 1.16.0
39
-
40
- Support explicitly trusted request origins so state-changing requests (e.g. login) work behind proxies that do not present the app's external host most notably GitHub Codespaces port forwarding (CA-90).
41
-
42
- * feat(web-framework): add `TI_WEB_TRUSTED_ORIGINS` (comma-separated) / `config.trustedOrigins`. The `originRefererValidationHandler` now accepts a non-GET request whose `Origin`/`Referer` matches the server-reconstructed base URL **or** any configured trusted origin. Previously such a request behind a proxy that rewrote/omitted the forwarded host was rejected with `E_WEB_INVALID_REQUEST_PARAMETERS` (HTTP 403). Backward compatible (empty list = prior behavior); the CSRF double-submit token check is unchanged and still enforced
43
- * build(release): bump package version from `1.15.0` to `1.16.0`
44
-
45
- ## Version 1.15.0
46
-
47
- A dedicated health endpoint, a `TI_WEB_AUTH_METHODS` env override, and login-page gating for every auth method completing the container-friendly auth/health story for the competence deployment (CA-90).
48
-
49
- * feat(web-framework): add an unprotected `GET /health` endpoint (`healthHandler`) that returns `200` with `{ status, broker, uptime }` — a purpose-built liveness/readiness probe for container and orchestrator health checks, so probes no longer have to hit the user-facing login route; `broker` reports the Redis connection state
50
- * feat(web-framework): add the `TI_WEB_AUTH_METHODS` env override (comma-separated) which REPLACES `auth.enabledMethods` — a clean, 12-factor way to select enabled auth methods per deployment (the config-file merge is by-index and cannot cleanly override an array)
51
- * feat(web-framework): extend login-page auth gating from the OAuth buttons to every method — the `local` credentials form and the "or continue with" divider are now gated too, and a "no method configured" fallback is shown when nothing is enabled, so an SSO-only deployment presents no dead local form
52
- * build(release): bump package version from `1.14.1` to `1.15.0`
53
-
54
- ## Version 1.14.1
55
-
56
- Security hardening for the web-server CodeQL findings raised after the scanner was modernized in CA-90 (CA-91).
57
-
58
- * fix(web-framework): rewrite the default unprotected static-asset route matchers from the ambiguous `(?:.+/)*` to segment-anchored `(?:[^/]+/)*` — the previous form backtracked exponentially and is evaluated against the raw request path in `isUnprotectedRoute()` before authentication, making it a pre-authentication denial-of-service vector (CodeQL js/redos); the matched language for realistic asset paths is unchanged, and the matchers are now the `RE_STATIC_UNPROTECTED` / `RE_WELL_KNOWN_UNPROTECTED` module constants
59
- * fix(web-framework): replace the login-fragment section stripper's `open[\s\S]*?close` global-regex removal with a linear `indexOf`-based `stripMarkerSpans()` helper (also used for the per-provider stripper), eliminating the polynomial-time rescan on hostile input (CodeQL js/polynomial-redos)
60
- * docs(web-framework): document that Helmet's built-in Content-Security-Policy is intentionally disabled because a per-request, nonce-based CSP is enforced by `cspHeaderHandler()` on the following middleware — added an explanatory comment and an inline CodeQL suppression (the alert is a false positive)
61
- * build(release): bump package version from `1.14.0` to `1.14.1`
62
-
63
- ## Version 1.14.0
64
-
65
- `TI_WEB_*` environment-variable overrides for the web server configuration, enabling 12-factor container deployments without per-environment config files (CA-90).
66
-
67
- * feat(web-framework): add `applyWebConfigEnvOverrides( config, env = process.env )` (`#web-config-env`) — a pure helper applying `TI_WEB_HOST`, `TI_WEB_PORT`, `TI_WEB_USE_TLS`, `TI_WEB_TLS_CERT_PATH`, `TI_WEB_TLS_KEY_PATH`, and `TI_WEB_COOKIE_SECRET` overrides onto the merged `TiWebServer` configuration, only when each variable is defined (fully backward compatible)
68
- * fix(web-framework): skip an enabled OpenID Connect provider that has no client ID instead of crashing the instance during discovery an OAuth-less deployment (e.g. a container started without OAuth credentials) now boots on its remaining methods, with a warning, and reports the dropped provider as unavailable so a sign-in attempt against it fails per-request rather than at startup
69
- * feat(web-framework): the login page now renders an OpenID provider button only when that provider is an effective enabled auth method — the web server passes the post-drop enabled methods to the app manager, which strips the Google/Azure button (and the whole "or continue with" section when no provider is available) from `frame-login.html` at render time
70
- * build(release): bump package version from `1.13.2` to `1.14.0`
71
-
72
- ## Version 1.13.0
73
-
74
- A reusable role-based screen gate and a per-screen title override (back the competence screen-access work and the evaluation/scores screen split).
75
-
76
- * feat(web-framework): the default `TiWebAppManager.verifyAccess` now enforces a fragment's declared `roles` — a fragment registered (via `addFragment`) with a `roles` array is served only to sessions holding at least one of them (otherwise rejected `E_SEC_UNAUTHORIZED_ACCESS` 403), while a fragment with no `roles` stays public. This makes role-restricted screens unreachable by direct URL, not merely hidden in the UI; apps just declare `roles` on `addFragment` — no `verifyAccess` override needed. Backward compatible: all existing role-less fragments remain public
77
- * feat(web-framework): add `authorization.isAccessAllowed( requiredRoles, userRoles )` — a pure, unit-tested access decision (empty/absent roles = public; otherwise ≥1 overlap; no implicit hierarchy, so an `admin` gate is never satisfied by a numeric role) that backs the default `verifyAccess`
78
- * feat(web-framework): add `tiApplication.setScreenTitle( title )` — a per-screen topbar/document-title override (cleared automatically on navigation) so a screen can correct its own title at runtime (e.g. a manager viewing another user's scores must not read "My …")
79
- * build(release): bump package version from `1.12.0` to `1.13.0`
80
-
81
- ## Version 1.12.0
82
-
83
- Chart primitives gain legends + value labels for grouped bars and a legend for radar (backs the leaner competence evaluation results view) (CA-61).
84
-
85
- * feat(web-framework): `ti-charts` grouped bars now render an optional swatch legend (`options.legend`) and per-bar value captions (`options.valueLabels`); radar charts render an optional legend, with a dashed swatch variant (`{ dashed: true }`) for dashed series such as an "expected" curve (CA-61)
86
- * feat(web-framework): add `.ti-chart-bar-seg.tone-info`, `.ti-chart-legend-swatch.tone-info`, and `.ti-chart-legend-swatch.is-dashed` so grouped/radar source series and their legends share one colour scale across both themes (CA-61)
87
- * feat(web-framework): `ti-charts` radar accepts an optional per-axis `tone`, applied as a `tone-*` class on the axis label so consumers can colour axis labels (e.g. by category) — threaded through `radarLayout` (CA-61)
88
- * feat(web-framework): `ti-charts` grouped bars accept optional `options.barThickness` (bar height) and `options.valueFontSize` (value-caption font); value captions carry a dedicated `ti-chart-bar-value` class that intentionally sets no CSS `font-size`, so the renderer's `font-size` presentation attribute (default 4) actually governs — previously the caption also carried `ti-chart-bar-label`, whose CSS `font-size: 4px` overrode the attribute and made `valueFontSize` a no-op (CA-61)
89
- * build(release): bump package version from `1.11.1` to `1.12.0`
90
-
91
- ## Version 1.11.1
92
-
93
- Post-review fix from the CA-72 CodeRabbit review (PR #85) on the login test-user panel.
94
-
95
- * fix(web-framework): turning the "override roles (dev)" toggle OFF now always strips any persisted roles from the `ti-test-user` cookie even when the selected employee is no longer in the panel's profile list — so a stale roles array can't keep overriding the app's org-derived roles on the next login (CA-72)
96
- * build(release): bump package version from `1.11.0` to `1.11.1`
97
-
98
- ## Version 1.11.0
99
-
100
- Login test-user panel defaults to identity-only injection so the app derives roles itself (CA-72).
101
-
102
- * feat(web-framework): login test-user panel injects identity only by default (roles derived by the app); role injection becomes an opt-in dev override (CA-72)
103
- * docs(web-framework): clarify the augmentSession contract (derive vs. override) (CA-72)
104
- * build(release): bump package version from `1.10.4` to `1.11.0`
105
-
106
- ## Version 1.10.4
107
-
108
- Login test-user fixture follow-up (CA-71).
109
-
110
- * chore(web-framework): login test-user `8` carries the `MANAGER` role (`[1, 2]`) so manager / self-manage scenarios (e.g. the competence Org Chart self-manage gate) can be exercised without re-seeding
111
- * build(release): bump package version from `1.10.3` to `1.10.4`
112
-
113
- ## Version 1.10.3
114
-
115
- Developer-tooling and formatting touch-ups on the shared frontend bundle.
116
-
117
- * chore(web-framework): the login test-user panel offers more seeded employees (3, 8, 9 alongside 22/20/1/4) so manager / direct-report / skip-level scenarios can be exercised without re-seeding
118
- * style(web-framework): minor formatting of `formatException`'s return object
119
- * build(release): bump package version from `1.10.2` to `1.10.3`
120
-
121
- ## Version 1.10.2
122
-
123
- Readability and scaling fixes for the chart primitives, surfaced while polishing the Statistics & Results screens (CA-61).
124
-
125
- * fix(web-framework): stacked bar charts now caption each row (the group/cycle label plus an optional per-row value) and render an optional swatch legend driven by `spec.options.legend`, so a coverage "By group" chart reads as labelled bars instead of anonymous colour blocks; the row labels also land on the cross-cycle trend bars
126
- * fix(css): horizontal bar charts opt out of the global `svg` `max-height` so bar thickness and label size stay identical regardless of row count — a tall org-wide chart is no longer uniformly scaled down and rendered finer than the same chart on a smaller subtree; the per-row geometry is trimmed for a cleaner look
127
- * feat(css): `.ti-chart-legend` / `.ti-chart-legend-item` / `.ti-chart-legend-swatch` — a chart swatch legend whose colours route through the inherited grade/ink chart tokens
128
- * build(release): bump package version from `1.10.1` to `1.10.2`
129
-
130
- ## Version 1.10.1
131
-
132
- Review fixes for the ti-chart primitives (Statistics & Results, CA-61, PR #83 CodeRabbit pass).
133
-
134
- * fix(web-framework): drillable heatmap/box marks get an accessible name; `renderChart` clears stale `data-ti-chart-empty`/`aria-label` on rerender; `renderStat` renders a missing value as an em dash; the provisional line-dot stroke follows its `tone-*` class
135
- * fix(css): `.ti-chart-sr` uses `clip-path: inset(50%)` instead of the deprecated `clip` property
136
- * build(release): bump package version from `1.10.0` to `1.10.1`
137
-
138
- ## Version 1.10.0
139
-
140
- ### Charting primitive library (Statistics & Results, CA-61)
141
-
142
- * feat(web-framework): new `ti-charts.js` — a CSP-safe SVG charting library backing the competence Statistics & Results reporting. Eight primitives via a single `renderChart(figure, spec)` dispatcher: `gauge`, `bars` (stacked / grouped / diverging modes), `stat`, `scatter`, `heatmap` (sequential / diverging scales), `box`, `radar`, and `line` (mean + p25–p75 band, sparkline, stacked, dashed-provisional trailing segment). The pure layout helpers (`gaugeArcPath`, `barSegments`, `scatterLayout`, `heatmapLayout`, `boxLayout`, `radarLayout`, `lineLayout`, …) are unit-tested in isolation
143
- * feat(web-framework): register the `x-ti-chart` Alpine CSP directive (binds a spec object to a host `<figure>`); every chart builds its SVG with `createElementNS` + `setAttribute` only (never `element.style.*` except `setProperty("--var")`) and ships a visually-hidden `.ti-chart-sr` accessibility table
144
- * feat(css): `.ti-chart-*` styles + per-type `figure[data-ti-chart-type]` size caps + `--chart-seq-1…5` sequential ramp tokens and grade/tone colours in both themes (daylight + black-glass)
145
- * build(release): bump package version from `1.9.3` to `1.10.0`
146
-
147
- ## Version 1.9.3
148
-
149
- * feat(css): an empty `.ti-grade-chip` (a competency whose rating is still awaited) now renders an hourglass glyph via `::before` instead of a literal dash, so "awaiting rating" reads as a clear visual state wherever an empty grade chip is shown to a permitted viewer
150
- * build(release): bump package version from `1.9.2` to `1.9.3`
151
-
152
- ## Version 1.9.2
153
-
154
- * feat(css): add `.ti-spacer` — a flexible spacer (`flex: 1 1 auto`) that pushes following siblings to the far end of a flex row/column. Promotes the bespoke per-screen `competence-empmgmt-actions-spacer` into a shared primitive, now consumed by the employee-management actions panel and the evaluation screen's team-feedback finalize bar
155
- * build(release): bump package version from `1.9.1` to `1.9.2`
156
-
157
- ## Version 1.9.1
158
-
159
- * build(deps): upgrade `ajv` from ^6.15.0 to ^8.20.0 — ajv 8 renamed the validation-error `dataPath` (dot style) to `instancePath` (JSON Pointer)
160
- * fix(config-registry): normalize ajv 8's `instancePath` back to the dot/bracket data path the registry has always exposed on schema issues (e.g. `.competencies.E1-1.name`, array indices as `[0]`), so the public `ConfigValidationIssue.path` contract is unchanged across the upgrade; the ajv compile options (`meta`, `schemaId: "$id"`, `validateSchema: false`) and Draft-07 handling are unchanged
161
- * build(deps): update `helmet` from ^8.1.0 to ^8.2.0
162
-
163
- ## Version 1.9.0
164
-
165
- * feat(css): add `.ti-panel-body-intro` the canonical description/intro line under a `.ti-panel-head` (`--fs-sm`, secondary foreground, `0 var(--s-3) var(--s-5)` padding, 1.5 line-height); replaces the per-screen intro paragraphs that screens used to hand-style
166
- * refactor(css): tighten the key/value primitives — `.ti-kv-label` is now an uppercase `--fs-xs` 600-weight caption (0.05em letter-spacing); `.ti-kv-value` is `--fs-sm` 400-weight — for a consistent, scannable key/value rhythm across screens
167
- * refactor(css): drop the redundant `margin-left: auto` from `.ti-panel-head-aside` (the panel head already positions it via its flex layout)
168
- * fix(sidebar): the user-profile flyout actions (Profile, Settings, Logout) now actually fire. The menu items bind their `hx-*` attributes through Alpine (`x-bind`), which HTMX does not pick up on its initial document scan — so the buttons previously only closed the flyout. The flyout now runs `htmx.process` on its panel when it opens (idempotent on re-open), wiring up each button's `hx-get`/`hx-post`/`hx-target`/`hx-swap`
169
- * fix(sidebar): the role/department line under the user name no longer overflows the fixed-width sidebar — `.ti-sidebar-user-name` and `.ti-sidebar-user-sub` truncate with an ellipsis, and `.ti-sidebar-user-text` gets `flex: 1` so it bounds the text column
170
- * style(css): expand the `.ti-icon` size-modifier one-liners (`.xs`/`.sm`/`.md`/`.lg`/`.xl`) to block form for consistency with the rest of the sheet
171
- * build(release): bump package version from `1.8.0` to `1.9.0`
172
-
173
- ## Version 1.8.0
174
-
175
- * feat(notifications): notifications can now show a secondary **details** line under the generic message. `tiApplication.formatException` returns `{ message, details }` (resolved from the exception's `data.details`, falling back to the raw text for non-localized messages) and `tiApplication.notify` accepts that payload so an error like "The request parameters are not recognized or not supported." now also shows the specifics (e.g. "Competency codes not in the 'QE' pool: …") in a smaller, muted font. The returned object stringifies to its message, so existing string usages keep working unchanged
176
- * fix(css): raise the toast stack above the modal layer (`z-index` 1100 → 1300; the modal backdrop is 1200) so a notification raised while a modal is open is no longer hidden behind it
177
-
178
- ## Version 1.7.1
179
-
180
- * fix(web-handlers): web-application request errors that carry no explicit `httpCode` are no longer reported as `500`. A new `resolveHttpCode` derives the status from the exception code — request-validation and application-logic errors (`E_WEB_*` / `E_APP_*`) map to `422 Unprocessable Content`, security (`E_SEC_*`) to `403`, resource not-found/already-exists to `404`/`409`, and method/URI/content errors to `405`/`404`/`415`; only genuine internal, communication, and unknown errors still default to `500`. An explicit `httpCode` on the exception always wins. Applied in both the `/app` request handler (`formatException`) and the default error handler
181
-
182
- ## Version 1.7.0
183
-
184
- * feat(css): introduce `.ti-data-grid` family — `.ti-data-grid`, `.ti-data-grid-head`, `.ti-data-grid-rows`, `.ti-data-grid-row` with shared `--ti-grid-cols` template; row state modifiers `.is-current` (accent-soft, "current user") and `.is-selected` (accent-soft + left accent bar); wrapper variants `.bordered` (horizontal dividers for tabular displays) and `.compact` (denser padding); cell utilities `.ti-cell-center` / `.ti-cell-right` for per-cell alignment
185
- * feat(css): introduce `.ti-page-head` as a vertical block stack (eyebrow above title, subtitle below) using `--fs-xs` for the eyebrow and clamping subtitle width at 60ch
186
- * feat(css): introduce a reusable `.ti-form*` family `.ti-form`, `.ti-form-section`, `.ti-form-section-title`, `.ti-form-grid` (with `.cols-1` / `.cols-3` modifiers), `.ti-form-row` (with `.wide` for grid-spanning), `.ti-form-readonly`, `.ti-form-hint`, `.ti-form-error`, `.ti-form-actions`, `.ti-form-state` (with `.saved` / `.unsaved`); single responsive collapse to one column under 720px
187
- * feat(css): extend `.ti-panel-head` with sub-elements — `.ti-panel-head-icon` (32x32 framed icon slot), `.ti-panel-head-text` (title + subtitle stack inside a flex row), `.ti-panel-title-aside` (inline qualifier next to the title), `.ti-panel-subtitle` (dimmed sub line), `.ti-panel-head-aside` (right-aligned read-only info with left-border separator), and the `.bar` modifier (sunken full-width banner)
188
- * feat(icons)!: rebase `.ti-icon` on `background-color: currentColor` so icons inherit the surrounding text colour; add size modifiers `.xs` (12px), `.sm` (14px), `.md` (16px), `.lg` (24px), `.xl` (32px); add `.legacy-gray` modifier to preserve the previous gray/hover behaviour for consumers that rely on the fixed colour scheme
189
- * feat(icons): add 21 new `.ti-icon` variants (lucide / feather style, 24x24 viewBox) `plus`, `close`, `check`, `check-clipboard`, `send`, `search`, `clock`, `warning-triangle`, `info-circle`, `bell`, `check-circle`, `eye`, `calendar-blank`, `user`, `users`, `briefcase`, `folder`, `book`, `help-circle`, `bar-chart`, `chevron-left`, `chevron-right`, `dashboard-grid`, `cycles-loop`, `sun`
190
- * feat(icons): add `.ti-icon.moon` mask variant so theme toggles can mirror the target mode
191
- * feat(framework): add `tiApplication.hasRole(roleCode)` helper that does the array-shape check in plain JS (the Alpine CSP build does not expose `Array` to its expression evaluator, so `Array.isArray(...)` written inline in a template raises `Undefined variable: Array`)
192
- * feat(framework): add `tiApplication.topbarPrimaryCta` store slot plus `setTopbarPrimaryCta` / `setTopbarPrimaryCtaDisabled` API for per-screen CTA buttons in the topbar; auto-cleared on screen navigation so each screen owns its slot
193
- * feat(css): native select chevron replaced by a custom down-chevron SVG positioned at right: 10px / 14x14; padding-right reserves the slot; glass theme overrides the SVG stroke colour because `background-image` can't pick up `currentColor`
194
- * feat(css): subdue `::-webkit-calendar-picker-indicator` to opacity 0.7 (1 on hover) so date-input visual weight matches the chevron
195
- * feat(notification bar): replace inline toast SVGs with `.ti-icon` mask classes (success check, danger close, warn triangle, info circle, close button)
196
- * feat(sidebar): replace inline navigation SVGs with `.ti-icon` mask classes (collapse chevron, dashboard home, sun theme toggle)
197
- * refactor(css): drop the screen-specific page-header, form, and tabular-layout CSS that duplicated framework primitives; all in-tree screens (`frame-employees-list`, `frame-cycles`, `frame-cycle-setup`, `frame-competence-evaluation`, `frame-new-evaluation`, `frame-manager-calendar`, `frame-interview-schedule`, `frame-employee-management`) now consume `.ti-page-head`, `.ti-data-grid`, `.ti-form*`, and `.ti-panel-head*` instead
198
- * docs(modal): doc-block on `.ti-modal-*` confirming it as the canonical shared primitive (introduced in 1.6.3 via the competence cycle-setup work)
199
- * build(release): bump package version from `1.6.3` to `1.7.0`
200
-
201
- ## Version 1.6.3
202
-
203
- * feat(css): add `--ti-internal-padding` CSS variable to the design token system
204
- * feat(css): add `--ti-border-color` CSS variable for consistent border theming
205
- * feat(icons): add `.ti-icon.calendar` and `.ti-icon.schedule` icon variants with hover states
206
- * feat(css): add `.ti-data-value.fill-space` modifier for flex-grow behavior in inline data value layouts
207
- * fix(css): remove `min-width: 120px` constraint from `.ti-button.inline` for more flexible sizing
208
- * fix(css): update z-index stacking values for dropdown and overlay elements to prevent layering conflicts
209
- * build(deps): update `openid-client` from ^6.8.2 to ^6.8.4
210
- * build(deps): update bundled `@alpinejs/csp` from ^3.15.11 to ^3.15.12
211
- * build(deps): update bundled `htmx.org` from ^2.0.8 to ^2.0.10
212
- * build(static): refresh bundled Alpine.js CSP and HTMX library files to match updated dependency versions
213
-
214
- ## Version 1.6.2
215
-
216
- * feat(ui): replace Material Symbols usage with framework-native `.ti-icon` classes across sidebar flyouts, login actions, and notification bar
217
- * feat(sidebar): merge administration and user flyout menus into a single `sidebarApplicationMenu` with configurable menu icon and updated actions
218
- * feat(css): add embedded SVG mask icon variants (`app-menu`, `dashboard`, `settings`, `error`, `user-profile`, `login`, `logout`, `internet`) and increase default icon size to `24px`
219
- * refactor(theme): remove Material Symbols-specific icon styling from the black-glass theme
220
- * build(static): remove external Google Material Symbols stylesheet import from static `index.html`
221
- * build(release): bump package version from `1.6.1` to `1.6.2`
222
-
223
- ## Version 1.6.1
224
-
225
- * feat(css): add inline button support via `.ti-button.inline` and new `--ti-button-inline-height` design token
226
- * refactor(ui): update sidebar flyout positioning logic to use shared `tiToolbox` viewport helpers (`getVisibleBox`, `clampToBox`)
227
- * fix(ui): fix the call to utility functions `getVisibleBox` and `clampToBox` in the sidebar flyout component
228
- * build(release): bump package version from `1.6.0` to `1.6.1`
229
-
230
- ## Version 1.6.0
231
-
232
- * feat(toolbox): add Alpine.js `tiToolbox` store with shared utility methods (`deepMerge`, `deepFreeze`, `structuredClone`, `formatDate`, viewport helpers, and cookie access)
233
- * feat(ui): move sidebar menu configuration into `ti-framework.js` and register `tiComponentsConfig` during Alpine.js initialization
234
- * refactor(static): remove legacy `ti-user-interface.js` from static assets and stop loading it from `index.html`
235
- * refactor(components): update framework components to consume toolbox utilities through Alpine stores
236
- * refactor(docs): add and expand JSDoc typedefs and method-level documentation in `ti-framework.js`
237
- * build(release): bump package version from `1.5.3` to `1.6.0`
238
-
239
- ## Version 1.5.3
240
-
241
- * feat(framework): add `openScreen` method for in-app navigation
242
- * fix(auth): add explicit HTTP `401` status to authentication failure
243
-
244
- ## Version 1.5.2
245
-
246
- * feat(framework): expand application API by improving `sendRequest`, `notify`, and `getLabel` methods
247
- * feat(tooltip): add helper methods `getTooltipMessage`, `handleEnter`, `handleLeave`, `showTooltip`, `hideTooltip` to the tooltip component
248
- * feat(css): improve styles and style structure
249
-
250
- ## Version 1.5.1
251
-
252
- * feat(framework): add `isValidDate` utility function for validating Date instances
253
- * feat(framework): add `deepFreeze` utility function for recursive object freezing
254
- * feat(framework): add `getLabel` method on application configuration for nested label resolution with dot notation
255
- * feat(framework): add Alpine.js directive `x-text-label` for runtime label translation
256
- * feat(framework): add a configuration object to replace labels object with enhanced structure
257
- * feat(config): add authentication state (`auth.isAuthenticated`) to config endpoint response
258
- * feat(placeholder): add inner content capture and injection for placeholder replacement
259
- * feat(tooltip): add a new tooltip component with Alpine.js integration and positioning
260
- * feat(css): add CSS custom properties for padding, margin, and font-family
261
- * feat(css): add tooltip styling variables (background, foreground, size, shadow, arrow)
262
- * feat(css): add `.ti-content.pane` block with flex layout and overflow handling
263
- * feat(css): add error color, flyout item shadows, and separator color variables
264
- * feat(css): add `.ti-glass-btn-black.large` variant with left-justified content
265
- * refactor(framework): change user initialization from `undefined` to `null`
266
- * refactor(framework): improve request failure handling with proper error rejection
267
- * refactor(css): replace hard-coded spacing with CSS variables across components
268
- * refactor(css): add `overflow: hidden` to body and `.ti-main` for better layout control
269
- * refactor(css): convert color and styling values to CSS variables throughout
270
- * refactor(handlers): add `convertUriToString` helper for safe URI object stringification
271
- * refactor(handlers): add a request context object (query, params, headers, url, method) to JSON responses
272
- * refactor(handlers): augment user data with default employeeID and roles in the user information handler
273
- * build(deps): update express from ^5.1.0 to ^5.2.1
274
- * build(deps): update express-session from ^1.18.2 to ^1.19.0
275
- * build(deps): update lodash from ^4.17.21 to ^4.17.23
276
- * build(deps): update openid-client from ^6.8.1 to ^6.8.2
277
- * build(deps): update `@alpinejs/csp` from ^3.15.2 to ^3.15.8
278
- * build(engines): update Node.js requirement from >=18.0.0 to >=20.0.0
279
-
280
- ## Version 1.5.0
281
-
282
- * feat(web-app)!: change `TiWebAppManager` to be an abstract class
283
- * feat(web-app): rename class `WebAppManager` to `TiWebAppManager` and add a static file caching mechanism
284
- * feat(web-app): add `addFragment` method with override protection for custom HTML fragment registration
285
- * feat(web-app): add `webAppIdentifier` getter to expose application identifier
286
- * feat(web-server): add support for dynamic web application instantiation from config via `classPath`
287
- * feat(web-server): add `defineWebApplicationRoutes` and `defineUnprotectedRoutes` extension points
288
- * feat(web-server): add `endpointEnabled` flag to conditionally enable API endpoint proxy
289
- * feat(web-server): add serving for `.well-known` directory for web standards compliance
290
- * feat(package): add public exports for `./web-application` and `./web-server` subpaths
291
- * feat(package): add `files` whitelist and repository metadata (homepage, bugs URL, git repository)
292
- * feat(package): add Node.js version requirement (>=18.0.0) via the `engines` field
293
- * feat(build): add a post-install script to vendor HTMX and Alpine.js CSP libraries locally
294
- * refactor(web-app): replace `fullPublicPath` with `staticContentPaths` array for multi-path static content resolution
295
- * refactor(web-app): add file location search algorithm with caching via `#locateStaticFile` method
296
- * refactor(web-app): update `transformHtml` signature to remove `fullPublicPath` parameter
297
- * refactor(web-app): update `assembleHtmlView` to accept `staticContentPaths` instead of `fullPublicPath`
298
- * refactor(web-server): replace the single static path with configurable `staticContentPaths` array
299
- * refactor(web-server): merge web server default config with the provided service config in constructor
300
- * refactor(web-server): load web server default config directly from the package JSON import instead of the ENV configuration
301
- * refactor(web-server): improve TLS initialization error handling to reject promise instead of throw
302
- * refactor(package): reorganize imports from `./server/...` to `./bin/...` and `./components/...` paths
303
- * refactor(package): move `@alpinejs/csp` from dependencies to devDependencies
304
- * build(static): replace CDN script references with local copies for HTMX and Alpine.js CSP
305
- * build(env): remove `TI_INSTANCE_CONFIG` and update `TI_LOCALIZATION_LABELS_PATH` to use `bin/localization/` path
306
- * build(env): add `TI_AUDITING_LOG_MIN_LEVEL` configuration variable
307
- * fix(ui): change `aria-expanded` binding from string to boolean in the sidebar flyout component
308
- * fix(ui): remove incorrect `type="button"` attribute from `Home` anchor element
309
- * docs: improve various documentation comments and class descriptions
310
-
311
- ## Version 1.4.0
312
-
313
- * feat(ui): add a notification bar component with Alpine.js integration and auto-dismiss functionality
314
- * feat(ui): add CSS variables and styles for the notification system
315
- * feat(routes): add `/not-found` fragment and route for 404 error pages
316
- * feat(routes): add `/app/error` route for error handling testing - will be removed later
317
- * feat(routes): add `/app/config` data endpoint for serving application configuration
318
- * feat(localization): add language property to `User` class with getter and JSON serialization
319
- * feat(localization): integrate localization module for label management and localized error messages
320
- * feat(session): add language property to session data populated from user or service configuration
321
- * feat(handlers): add response type detection helper `isAcceptingResponseType` for content negotiation
322
- * feat(handlers): enhance error responses with localized messages via localization module
323
- * feat(handlers): add HTMX-aware error handling with HX-Redirect and HX-Retarget headers
324
- * feat(handlers): implement `processDataRequest` method in WebAppManager for serving data resources
325
- * feat(config): add a language configuration option to the `WebServiceConfiguration` object
326
- * refactor(handlers): delegate error handling from resource protection to error middleware
327
- * refactor(handlers): improve 401/404 response handling by routing through exceptions and middleware
328
- * refactor(handlers): enhance CSRF and origin validation to use error middleware instead of direct responses
329
- * refactor(handlers): improve service call error handling to raise exceptions and delegate to middleware
330
- * refactor(handlers): refactor invalid route handler to raise exceptions instead of direct 404 responses
331
- * fix(types): correct ExpressRequest typedef from `import("express").req` to `import("express").Request`
332
- * build(config): update publicPath from `packages/web-framework/bin/static` to `bin/static`
333
- * build(config): update TLS certificate paths to use relative `bin/tls/` paths
334
- * build(env): update `TI_INSTANCE_CLASS` and `TI_INSTANCE_CONFIG` paths to use relative `bin/` paths
335
- * build(env): add `TI_LOCALIZATION_LABELS_PATH` environment variable for custom labels
336
- * build(run): update IDE run configuration to use relative paths and the correct working directory
337
- * build(labels): add an empty `web-server-labels.json` file for custom localization labels
338
-
339
- ## Version 1.3.0
340
-
341
- * feat: first working prototype version
1
+ # ti-engine web-framework changelog
2
+
3
+ This document will contain the list of changes made to the framework. The format is based on the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.
4
+
5
+ ## Version 1.19.1
6
+
7
+ Documentation and packaging onlyno functional change.
8
+
9
+ The `SemanticValidator` fix was found while attempting to generate TypeScript declarations from the framework's JSDoc. That work is **deferred to a later release**, but this correction stands on its own: the typedef was written as `function(Object, ValidatorContext): (ConfigValidationIssue[]|Promise<ConfigValidationIssue[]>)`, whose parenthesised return type inside the Closure form no parser can read. It described the validator contract correctly to a human reader and not at all to a machine.
10
+
11
+ * fix(config-registry): rewrite the `SemanticValidator` typedef in arrow syntax
12
+ * feat(package): declare `keywords`, which the package had none of the terms npm search matches against
13
+
14
+ ## Version 1.19.0
15
+
16
+ `/static` was served with `max-age=1y, immutable` for every consumer of the framework. `immutable` is a promise that the bytes behind a URL will never change, and browsers honour it so completely that not even a manual reload revalidates — so the promise is only true for a content-addressed URL (`app.a1b2c3.css`). None of the framework's own assets are named that way (`/static/scripts/ti-framework.js`, the theme sheets), which made this an unsafe default that shipped to npm: a deployed CSS or JS fix would never reach anyone who had already visited, for up to a year, with no way to tell them otherwise. The standalone author's site had worked around it privately by fingerprinting its own asset URLs; every other consumer still inherited the bug.
17
+
18
+ * fix(web-server)!: the default `/static` cache policy is now `public, max-age=0, must-revalidate` instead of `max-age=1y, immutable`, so a deployed asset change actually reaches a returning visitor. `express.static` still attaches an `ETag`/`Last-Modified`, so a revalidation of an unchanged asset is answered with a `304` — headers, no body. **A consumer whose asset filenames are content-addressed should opt back in** with `staticCache: { maxAge: 31536000, immutable: true }`; one that appends a content hash to its asset URLs (rather than to the filenames) is equally entitled to it
19
+ * feat(web-server): add the `staticCache` configuration block — `maxAge` (whole **seconds**, mapping 1:1 onto the `Cache-Control` directive; an express-style `"1y"` duration string is reported rather than silently reinterpreted as milliseconds), `immutable`, and `immutablePaths` (path prefixes served long-lived and `immutable` regardless of the other two). `immutable` combined with a `maxAge` of 0 is a contradiction and is dropped with a warning, so a half-configured deployment costs a revalidation rather than a year of unreachable assets
20
+ * feat(web-server): `staticCache.immutablePaths` defaults to `[ "/fonts/" ]` — a released `.woff2` is an artifact rather than something edited in place, and its filename already carries the family, weight and style. Configurable, and clearable with an explicitly empty array, because that is a statement about how a given deployment manages its font files
21
+ * feat(web-framework): add the `TI_WEB_STATIC_MAX_AGE`, `TI_WEB_STATIC_IMMUTABLE`, and `TI_WEB_STATIC_IMMUTABLE_PATHS` environment overrides, so the cache policy is settable per deployment like every other web setting; `TI_WEB_STATIC_IMMUTABLE_PATHS` **replaces** the array, and an explicitly empty value means no long-lived paths
22
+ * refactor(web-server): the `/static` mounts write `Cache-Control` per file through `express.static`'s `setHeaders` rather than its `maxAge`/`immutable` options, since the policy is no longer uniform across the tree; the decision itself lives in the pure, unit-tested `TiWebServer.resolveStaticCachePolicy()` (config → policy + warnings, which the caller logs) and `TiWebServer.staticCacheControlFor()` (policy + file → header). The `staticCache` defaults deliberately live on the class rather than in `web-server.json`, because the constructor's `_.merge` merges arrays by index and a consumer's empty `immutablePaths` could otherwise never clear a default entry
23
+ * docs(web-framework): document the `staticCache` block, the revalidating default and the reasoning behind it, and the fingerprinting opt-in in the README
24
+ * build(release): bump package version from `1.18.1` to `1.19.0`
25
+
26
+ ## Version 1.18.1
27
+
28
+ Enabling Azure SSO took the instance down at startup: the OAuth2 callback was registered by handing the configured callback value straight to Express as a route path, and the installation docs tell operators to set that value to the full absolute URL registered with the identity provider. Express 5 parses route patterns with path-to-regexp v8, where `:` opens a parameter name — so `https://host/login/azure-callback` throws `Missing parameter name at index 6` and the web server never starts. Google was affected identically, which also made this a prerequisite for the competence Cloud Run deployment, whose `deploy.sh` patches in an absolute callback URL (CA-97).
29
+
30
+ * fix(web-framework): register an OAuth2 callback by its **path** rather than by the configured value verbatim, so a callback given as the absolute URL registered with the provider no longer crashes startup; a callback that yields no usable path now logs a WARNING and skips that provider's endpoint instead of taking the instance down, matching how an enabled-but-unconfigured provider is already handled
31
+ * feat(web-framework): add `AuthManager.getOAuth2CallbackPath( authMethod )` and the pure, unit-tested `AuthManager.toCallbackPath( callbackUrl )` — reduces an absolute, protocol-relative, path or bare relative callback to its route path (query string and fragment stripped), or `null` when no usable path can be derived. The `redirect_uri` sent to the provider is deliberately left as configured, so an absolute callback keeps matching the provider registration exactly instead of depending on the forwarded protocol/host being correct
32
+ * docs(web-framework): document the OpenID Connect provider variables in the README, and state in the README, the competence `INSTALL.md` and `.env.example` that a callback URL may be given as either the absolute registered URL or a path — including what each implies for the `redirect_uri`, and that the path must be the one the app actually receives when a proxy strips a prefix
33
+ * build(release): bump package version from `1.18.0` to `1.18.1`
34
+
35
+ ## Version 1.18.0
36
+
37
+ Every web-server setting a container deployment needs could be supplied per environment except one: the admin allowlist. `auth.admins` was readable only from the config file baked into the image, so a containerized deployment had no way to name an administrator — leaving the admin configuration screens unreachable, or forcing a real identity to be committed to the repository. This closes that gap in the existing `TI_WEB_*` override set (CA-94).
38
+
39
+ * feat(web-framework): add the `TI_WEB_AUTH_ADMINS` environment override — comma-separated, **replaces** `auth.admins` (matched against the session user's user ID, username or email), so the admin allowlist is configurable per environment like every other web setting; an explicitly empty value means no admins
40
+ * docs(web-framework): document `TI_WEB_AUTH_METHODS` and `TI_WEB_TRUSTED_ORIGINS` in the README's environment-variable list, which had never listed them
41
+ * build(release): bump package version from `1.17.1` to `1.18.0`
42
+
43
+ ## Version 1.17.1
44
+
45
+ A validator that needs to compare its own config document against its previously committed state had no way to do so: `applyEdits`'s cross-document context resolves `getConfig` to the *pending* value for any document inside the current edit batch — by design, so a validator can check a sibling document's post-edit state — but a document is always part of its own edit batch, so calling `getConfig` on itself just hands back the same incoming value already passed as the validator's argument, never its prior state. This silently defeated the competence `research-consent` config's version-bump guard (CA-93).
46
+
47
+ * feat(web-framework): add `getStoredConfig(key)` to the `applyEdits` validator context (`ConfigService`) always resolves the current *committed* value from the store, even for the document currently under validation, so a validator comparing its own document against its previous state has a way to do it; purely additive — `getConfig`'s existing cross-document (pending-value) semantics are unchanged
48
+ * build(release): bump package version from `1.17.0` to `1.17.1`
49
+
50
+ ## Version 1.17.0
51
+
52
+ Route-registration seams so an application subclass can add its own Express routes and unprotected-route patterns — enabling public, content-driven sites (the first consumer being the standalone author's site) to layer a catch-all content resolver over the framework without reaching into private state.
53
+
54
+ * feat(web-server): add `TiWebServer.registerRoute( method, path, ...handlers )` — registers a custom route on the underlying Express app from a `defineWebApplicationRoutes()` override (after `super()`), so a catch-all resolver can be mounted after the framework's own routes but before its `*splat` 404 handler. Limited to route-scoped verbs (get/post/put/patch/delete/options/head/all); raises `E_GEN_INVALID_ARGUMENT_TYPE` for any other method and `E_GEN_NOT_INITIALIZED` if called before the Express app exists. The method must be a **string** — a non-string is rejected outright rather than coerced, so a value whose `toString()` happens to yield a verb (`[ "get" ]`, `new String( "get" )`) cannot slip past the allowlist and register a route
55
+ * feat(web-server): add `TiWebServer.addUnprotectedRoute( pattern )` — appends a string (exact-match) or RegExp (tested) pattern to the unprotected-routes list from a `defineUnprotectedRoutes()` override, so a public-by-default site can invert the framework's protect-by-default stance; non-string/non-RegExp values are ignored with a warning
56
+ * refactor(web-server): extract the unprotected-route matching loop from `isUnprotectedRoute()` into the pure, unit-tested `isRouteInList()` helper (behavior unchanged, including the defensive `lastIndex` reset), and add the `normalizeRegistrableMethod()` helper — both exported for testing alongside the existing `RE_*` matcher constants
57
+ * build(release): bump package version from `1.16.0` to `1.17.0`
58
+
59
+ ## Version 1.16.0
60
+
61
+ Support explicitly trusted request origins so state-changing requests (e.g. login) work behind proxies that do not present the app's external host — most notably GitHub Codespaces port forwarding (CA-90).
62
+
63
+ * feat(web-framework): add `TI_WEB_TRUSTED_ORIGINS` (comma-separated) / `config.trustedOrigins`. The `originRefererValidationHandler` now accepts a non-GET request whose `Origin`/`Referer` matches the server-reconstructed base URL **or** any configured trusted origin. Previously such a request behind a proxy that rewrote/omitted the forwarded host was rejected with `E_WEB_INVALID_REQUEST_PARAMETERS` (HTTP 403). Backward compatible (empty list = prior behavior); the CSRF double-submit token check is unchanged and still enforced
64
+ * build(release): bump package version from `1.15.0` to `1.16.0`
65
+
66
+ ## Version 1.15.0
67
+
68
+ A dedicated health endpoint, a `TI_WEB_AUTH_METHODS` env override, and login-page gating for every auth methodcompleting the container-friendly auth/health story for the competence deployment (CA-90).
69
+
70
+ * feat(web-framework): add an unprotected `GET /health` endpoint (`healthHandler`) that returns `200` with `{ status, broker, uptime }` — a purpose-built liveness/readiness probe for container and orchestrator health checks, so probes no longer have to hit the user-facing login route; `broker` reports the Redis connection state
71
+ * feat(web-framework): add the `TI_WEB_AUTH_METHODS` env override (comma-separated) which REPLACES `auth.enabledMethods` — a clean, 12-factor way to select enabled auth methods per deployment (the config-file merge is by-index and cannot cleanly override an array)
72
+ * feat(web-framework): extend login-page auth gating from the OAuth buttons to every method — the `local` credentials form and the "or continue with" divider are now gated too, and a "no method configured" fallback is shown when nothing is enabled, so an SSO-only deployment presents no dead local form
73
+ * build(release): bump package version from `1.14.1` to `1.15.0`
74
+
75
+ ## Version 1.14.1
76
+
77
+ Security hardening for the web-server CodeQL findings raised after the scanner was modernized in CA-90 (CA-91).
78
+
79
+ * fix(web-framework): rewrite the default unprotected static-asset route matchers from the ambiguous `(?:.+/)*` to segment-anchored `(?:[^/]+/)*` — the previous form backtracked exponentially and is evaluated against the raw request path in `isUnprotectedRoute()` before authentication, making it a pre-authentication denial-of-service vector (CodeQL js/redos); the matched language for realistic asset paths is unchanged, and the matchers are now the `RE_STATIC_UNPROTECTED` / `RE_WELL_KNOWN_UNPROTECTED` module constants
80
+ * fix(web-framework): replace the login-fragment section stripper's `open[\s\S]*?close` global-regex removal with a linear `indexOf`-based `stripMarkerSpans()` helper (also used for the per-provider stripper), eliminating the polynomial-time rescan on hostile input (CodeQL js/polynomial-redos)
81
+ * docs(web-framework): document that Helmet's built-in Content-Security-Policy is intentionally disabled because a per-request, nonce-based CSP is enforced by `cspHeaderHandler()` on the following middleware — added an explanatory comment and an inline CodeQL suppression (the alert is a false positive)
82
+ * build(release): bump package version from `1.14.0` to `1.14.1`
83
+
84
+ ## Version 1.14.0
85
+
86
+ `TI_WEB_*` environment-variable overrides for the web server configuration, enabling 12-factor container deployments without per-environment config files (CA-90).
87
+
88
+ * feat(web-framework): add `applyWebConfigEnvOverrides( config, env = process.env )` (`#web-config-env`) a pure helper applying `TI_WEB_HOST`, `TI_WEB_PORT`, `TI_WEB_USE_TLS`, `TI_WEB_TLS_CERT_PATH`, `TI_WEB_TLS_KEY_PATH`, and `TI_WEB_COOKIE_SECRET` overrides onto the merged `TiWebServer` configuration, only when each variable is defined (fully backward compatible)
89
+ * fix(web-framework): skip an enabled OpenID Connect provider that has no client ID instead of crashing the instance during discovery — an OAuth-less deployment (e.g. a container started without OAuth credentials) now boots on its remaining methods, with a warning, and reports the dropped provider as unavailable so a sign-in attempt against it fails per-request rather than at startup
90
+ * feat(web-framework): the login page now renders an OpenID provider button only when that provider is an effective enabled auth method — the web server passes the post-drop enabled methods to the app manager, which strips the Google/Azure button (and the whole "or continue with" section when no provider is available) from `frame-login.html` at render time
91
+ * build(release): bump package version from `1.13.2` to `1.14.0`
92
+
93
+ ## Version 1.13.0
94
+
95
+ A reusable role-based screen gate and a per-screen title override (back the competence screen-access work and the evaluation/scores screen split).
96
+
97
+ * feat(web-framework): the default `TiWebAppManager.verifyAccess` now enforces a fragment's declared `roles` — a fragment registered (via `addFragment`) with a `roles` array is served only to sessions holding at least one of them (otherwise rejected `E_SEC_UNAUTHORIZED_ACCESS` 403), while a fragment with no `roles` stays public. This makes role-restricted screens unreachable by direct URL, not merely hidden in the UI; apps just declare `roles` on `addFragment` — no `verifyAccess` override needed. Backward compatible: all existing role-less fragments remain public
98
+ * feat(web-framework): add `authorization.isAccessAllowed( requiredRoles, userRoles )` — a pure, unit-tested access decision (empty/absent roles = public; otherwise ≥1 overlap; no implicit hierarchy, so an `admin` gate is never satisfied by a numeric role) that backs the default `verifyAccess`
99
+ * feat(web-framework): add `tiApplication.setScreenTitle( title )` — a per-screen topbar/document-title override (cleared automatically on navigation) so a screen can correct its own title at runtime (e.g. a manager viewing another user's scores must not read "My …")
100
+ * build(release): bump package version from `1.12.0` to `1.13.0`
101
+
102
+ ## Version 1.12.0
103
+
104
+ Chart primitives gain legends + value labels for grouped bars and a legend for radar (backs the leaner competence evaluation results view) (CA-61).
105
+
106
+ * feat(web-framework): `ti-charts` grouped bars now render an optional swatch legend (`options.legend`) and per-bar value captions (`options.valueLabels`); radar charts render an optional legend, with a dashed swatch variant (`{ dashed: true }`) for dashed series such as an "expected" curve (CA-61)
107
+ * feat(web-framework): add `.ti-chart-bar-seg.tone-info`, `.ti-chart-legend-swatch.tone-info`, and `.ti-chart-legend-swatch.is-dashed` so grouped/radar source series and their legends share one colour scale across both themes (CA-61)
108
+ * feat(web-framework): `ti-charts` radar accepts an optional per-axis `tone`, applied as a `tone-*` class on the axis label so consumers can colour axis labels (e.g. by category) — threaded through `radarLayout` (CA-61)
109
+ * feat(web-framework): `ti-charts` grouped bars accept optional `options.barThickness` (bar height) and `options.valueFontSize` (value-caption font); value captions carry a dedicated `ti-chart-bar-value` class that intentionally sets no CSS `font-size`, so the renderer's `font-size` presentation attribute (default 4) actually governs — previously the caption also carried `ti-chart-bar-label`, whose CSS `font-size: 4px` overrode the attribute and made `valueFontSize` a no-op (CA-61)
110
+ * build(release): bump package version from `1.11.1` to `1.12.0`
111
+
112
+ ## Version 1.11.1
113
+
114
+ Post-review fix from the CA-72 CodeRabbit review (PR #85) on the login test-user panel.
115
+
116
+ * fix(web-framework): turning the "override roles (dev)" toggle OFF now always strips any persisted roles from the `ti-test-user` cookie — even when the selected employee is no longer in the panel's profile list — so a stale roles array can't keep overriding the app's org-derived roles on the next login (CA-72)
117
+ * build(release): bump package version from `1.11.0` to `1.11.1`
118
+
119
+ ## Version 1.11.0
120
+
121
+ Login test-user panel defaults to identity-only injection so the app derives roles itself (CA-72).
122
+
123
+ * feat(web-framework): login test-user panel injects identity only by default (roles derived by the app); role injection becomes an opt-in dev override (CA-72)
124
+ * docs(web-framework): clarify the augmentSession contract (derive vs. override) (CA-72)
125
+ * build(release): bump package version from `1.10.4` to `1.11.0`
126
+
127
+ ## Version 1.10.4
128
+
129
+ Login test-user fixture follow-up (CA-71).
130
+
131
+ * chore(web-framework): login test-user `8` carries the `MANAGER` role (`[1, 2]`) so manager / self-manage scenarios (e.g. the competence Org Chart self-manage gate) can be exercised without re-seeding
132
+ * build(release): bump package version from `1.10.3` to `1.10.4`
133
+
134
+ ## Version 1.10.3
135
+
136
+ Developer-tooling and formatting touch-ups on the shared frontend bundle.
137
+
138
+ * chore(web-framework): the login test-user panel offers more seeded employees (3, 8, 9 alongside 22/20/1/4) so manager / direct-report / skip-level scenarios can be exercised without re-seeding
139
+ * style(web-framework): minor formatting of `formatException`'s return object
140
+ * build(release): bump package version from `1.10.2` to `1.10.3`
141
+
142
+ ## Version 1.10.2
143
+
144
+ Readability and scaling fixes for the chart primitives, surfaced while polishing the Statistics & Results screens (CA-61).
145
+
146
+ * fix(web-framework): stacked bar charts now caption each row (the group/cycle label plus an optional per-row value) and render an optional swatch legend driven by `spec.options.legend`, so a coverage "By group" chart reads as labelled bars instead of anonymous colour blocks; the row labels also land on the cross-cycle trend bars
147
+ * fix(css): horizontal bar charts opt out of the global `svg` `max-height` so bar thickness and label size stay identical regardless of row count — a tall org-wide chart is no longer uniformly scaled down and rendered finer than the same chart on a smaller subtree; the per-row geometry is trimmed for a cleaner look
148
+ * feat(css): `.ti-chart-legend` / `.ti-chart-legend-item` / `.ti-chart-legend-swatch` — a chart swatch legend whose colours route through the inherited grade/ink chart tokens
149
+ * build(release): bump package version from `1.10.1` to `1.10.2`
150
+
151
+ ## Version 1.10.1
152
+
153
+ Review fixes for the ti-chart primitives (Statistics & Results, CA-61, PR #83 — CodeRabbit pass).
154
+
155
+ * fix(web-framework): drillable heatmap/box marks get an accessible name; `renderChart` clears stale `data-ti-chart-empty`/`aria-label` on rerender; `renderStat` renders a missing value as an em dash; the provisional line-dot stroke follows its `tone-*` class
156
+ * fix(css): `.ti-chart-sr` uses `clip-path: inset(50%)` instead of the deprecated `clip` property
157
+ * build(release): bump package version from `1.10.0` to `1.10.1`
158
+
159
+ ## Version 1.10.0
160
+
161
+ ### Charting primitive library (Statistics & Results, CA-61)
162
+
163
+ * feat(web-framework): new `ti-charts.js` — a CSP-safe SVG charting library backing the competence Statistics & Results reporting. Eight primitives via a single `renderChart(figure, spec)` dispatcher: `gauge`, `bars` (stacked / grouped / diverging modes), `stat`, `scatter`, `heatmap` (sequential / diverging scales), `box`, `radar`, and `line` (mean + p25–p75 band, sparkline, stacked, dashed-provisional trailing segment). The pure layout helpers (`gaugeArcPath`, `barSegments`, `scatterLayout`, `heatmapLayout`, `boxLayout`, `radarLayout`, `lineLayout`, …) are unit-tested in isolation
164
+ * feat(web-framework): register the `x-ti-chart` Alpine CSP directive (binds a spec object to a host `<figure>`); every chart builds its SVG with `createElementNS` + `setAttribute` only (never `element.style.*` except `setProperty("--var")`) and ships a visually-hidden `.ti-chart-sr` accessibility table
165
+ * feat(css): `.ti-chart-*` styles + per-type `figure[data-ti-chart-type]` size caps + `--chart-seq-1…5` sequential ramp tokens and grade/tone colours in both themes (daylight + black-glass)
166
+ * build(release): bump package version from `1.9.3` to `1.10.0`
167
+
168
+ ## Version 1.9.3
169
+
170
+ * feat(css): an empty `.ti-grade-chip` (a competency whose rating is still awaited) now renders an hourglass glyph via `::before` instead of a literal dash, so "awaiting rating" reads as a clear visual state wherever an empty grade chip is shown to a permitted viewer
171
+ * build(release): bump package version from `1.9.2` to `1.9.3`
172
+
173
+ ## Version 1.9.2
174
+
175
+ * feat(css): add `.ti-spacer` a flexible spacer (`flex: 1 1 auto`) that pushes following siblings to the far end of a flex row/column. Promotes the bespoke per-screen `competence-empmgmt-actions-spacer` into a shared primitive, now consumed by the employee-management actions panel and the evaluation screen's team-feedback finalize bar
176
+ * build(release): bump package version from `1.9.1` to `1.9.2`
177
+
178
+ ## Version 1.9.1
179
+
180
+ * build(deps): upgrade `ajv` from ^6.15.0 to ^8.20.0 ajv 8 renamed the validation-error `dataPath` (dot style) to `instancePath` (JSON Pointer)
181
+ * fix(config-registry): normalize ajv 8's `instancePath` back to the dot/bracket data path the registry has always exposed on schema issues (e.g. `.competencies.E1-1.name`, array indices as `[0]`), so the public `ConfigValidationIssue.path` contract is unchanged across the upgrade; the ajv compile options (`meta`, `schemaId: "$id"`, `validateSchema: false`) and Draft-07 handling are unchanged
182
+ * build(deps): update `helmet` from ^8.1.0 to ^8.2.0
183
+
184
+ ## Version 1.9.0
185
+
186
+ * feat(css): add `.ti-panel-body-intro`the canonical description/intro line under a `.ti-panel-head` (`--fs-sm`, secondary foreground, `0 var(--s-3) var(--s-5)` padding, 1.5 line-height); replaces the per-screen intro paragraphs that screens used to hand-style
187
+ * refactor(css): tighten the key/value primitives — `.ti-kv-label` is now an uppercase `--fs-xs` 600-weight caption (0.05em letter-spacing); `.ti-kv-value` is `--fs-sm` 400-weight for a consistent, scannable key/value rhythm across screens
188
+ * refactor(css): drop the redundant `margin-left: auto` from `.ti-panel-head-aside` (the panel head already positions it via its flex layout)
189
+ * fix(sidebar): the user-profile flyout actions (Profile, Settings, Logout) now actually fire. The menu items bind their `hx-*` attributes through Alpine (`x-bind`), which HTMX does not pick up on its initial document scan — so the buttons previously only closed the flyout. The flyout now runs `htmx.process` on its panel when it opens (idempotent on re-open), wiring up each button's `hx-get`/`hx-post`/`hx-target`/`hx-swap`
190
+ * fix(sidebar): the role/department line under the user name no longer overflows the fixed-width sidebar — `.ti-sidebar-user-name` and `.ti-sidebar-user-sub` truncate with an ellipsis, and `.ti-sidebar-user-text` gets `flex: 1` so it bounds the text column
191
+ * style(css): expand the `.ti-icon` size-modifier one-liners (`.xs`/`.sm`/`.md`/`.lg`/`.xl`) to block form for consistency with the rest of the sheet
192
+ * build(release): bump package version from `1.8.0` to `1.9.0`
193
+
194
+ ## Version 1.8.0
195
+
196
+ * feat(notifications): notifications can now show a secondary **details** line under the generic message. `tiApplication.formatException` returns `{ message, details }` (resolved from the exception's `data.details`, falling back to the raw text for non-localized messages) and `tiApplication.notify` accepts that payload — so an error like "The request parameters are not recognized or not supported." now also shows the specifics (e.g. "Competency codes not in the 'QE' pool: …") in a smaller, muted font. The returned object stringifies to its message, so existing string usages keep working unchanged
197
+ * fix(css): raise the toast stack above the modal layer (`z-index` 1100 1300; the modal backdrop is 1200) so a notification raised while a modal is open is no longer hidden behind it
198
+
199
+ ## Version 1.7.1
200
+
201
+ * fix(web-handlers): web-application request errors that carry no explicit `httpCode` are no longer reported as `500`. A new `resolveHttpCode` derives the status from the exception code — request-validation and application-logic errors (`E_WEB_*` / `E_APP_*`) map to `422 Unprocessable Content`, security (`E_SEC_*`) to `403`, resource not-found/already-exists to `404`/`409`, and method/URI/content errors to `405`/`404`/`415`; only genuine internal, communication, and unknown errors still default to `500`. An explicit `httpCode` on the exception always wins. Applied in both the `/app` request handler (`formatException`) and the default error handler
202
+
203
+ ## Version 1.7.0
204
+
205
+ * feat(css): introduce `.ti-data-grid` family `.ti-data-grid`, `.ti-data-grid-head`, `.ti-data-grid-rows`, `.ti-data-grid-row` with shared `--ti-grid-cols` template; row state modifiers `.is-current` (accent-soft, "current user") and `.is-selected` (accent-soft + left accent bar); wrapper variants `.bordered` (horizontal dividers for tabular displays) and `.compact` (denser padding); cell utilities `.ti-cell-center` / `.ti-cell-right` for per-cell alignment
206
+ * feat(css): introduce `.ti-page-head` as a vertical block stack (eyebrow above title, subtitle below) using `--fs-xs` for the eyebrow and clamping subtitle width at 60ch
207
+ * feat(css): introduce a reusable `.ti-form*` family — `.ti-form`, `.ti-form-section`, `.ti-form-section-title`, `.ti-form-grid` (with `.cols-1` / `.cols-3` modifiers), `.ti-form-row` (with `.wide` for grid-spanning), `.ti-form-readonly`, `.ti-form-hint`, `.ti-form-error`, `.ti-form-actions`, `.ti-form-state` (with `.saved` / `.unsaved`); single responsive collapse to one column under 720px
208
+ * feat(css): extend `.ti-panel-head` with sub-elements `.ti-panel-head-icon` (32x32 framed icon slot), `.ti-panel-head-text` (title + subtitle stack inside a flex row), `.ti-panel-title-aside` (inline qualifier next to the title), `.ti-panel-subtitle` (dimmed sub line), `.ti-panel-head-aside` (right-aligned read-only info with left-border separator), and the `.bar` modifier (sunken full-width banner)
209
+ * feat(icons)!: rebase `.ti-icon` on `background-color: currentColor` so icons inherit the surrounding text colour; add size modifiers `.xs` (12px), `.sm` (14px), `.md` (16px), `.lg` (24px), `.xl` (32px); add `.legacy-gray` modifier to preserve the previous gray/hover behaviour for consumers that rely on the fixed colour scheme
210
+ * feat(icons): add 21 new `.ti-icon` variants (lucide / feather style, 24x24 viewBox) — `plus`, `close`, `check`, `check-clipboard`, `send`, `search`, `clock`, `warning-triangle`, `info-circle`, `bell`, `check-circle`, `eye`, `calendar-blank`, `user`, `users`, `briefcase`, `folder`, `book`, `help-circle`, `bar-chart`, `chevron-left`, `chevron-right`, `dashboard-grid`, `cycles-loop`, `sun`
211
+ * feat(icons): add `.ti-icon.moon` mask variant so theme toggles can mirror the target mode
212
+ * feat(framework): add `tiApplication.hasRole(roleCode)` helper that does the array-shape check in plain JS (the Alpine CSP build does not expose `Array` to its expression evaluator, so `Array.isArray(...)` written inline in a template raises `Undefined variable: Array`)
213
+ * feat(framework): add `tiApplication.topbarPrimaryCta` store slot plus `setTopbarPrimaryCta` / `setTopbarPrimaryCtaDisabled` API for per-screen CTA buttons in the topbar; auto-cleared on screen navigation so each screen owns its slot
214
+ * feat(css): native select chevron replaced by a custom down-chevron SVG positioned at right: 10px / 14x14; padding-right reserves the slot; glass theme overrides the SVG stroke colour because `background-image` can't pick up `currentColor`
215
+ * feat(css): subdue `::-webkit-calendar-picker-indicator` to opacity 0.7 (1 on hover) so date-input visual weight matches the chevron
216
+ * feat(notification bar): replace inline toast SVGs with `.ti-icon` mask classes (success check, danger close, warn triangle, info circle, close button)
217
+ * feat(sidebar): replace inline navigation SVGs with `.ti-icon` mask classes (collapse chevron, dashboard home, sun theme toggle)
218
+ * refactor(css): drop the screen-specific page-header, form, and tabular-layout CSS that duplicated framework primitives; all in-tree screens (`frame-employees-list`, `frame-cycles`, `frame-cycle-setup`, `frame-competence-evaluation`, `frame-new-evaluation`, `frame-manager-calendar`, `frame-interview-schedule`, `frame-employee-management`) now consume `.ti-page-head`, `.ti-data-grid`, `.ti-form*`, and `.ti-panel-head*` instead
219
+ * docs(modal): doc-block on `.ti-modal-*` confirming it as the canonical shared primitive (introduced in 1.6.3 via the competence cycle-setup work)
220
+ * build(release): bump package version from `1.6.3` to `1.7.0`
221
+
222
+ ## Version 1.6.3
223
+
224
+ * feat(css): add `--ti-internal-padding` CSS variable to the design token system
225
+ * feat(css): add `--ti-border-color` CSS variable for consistent border theming
226
+ * feat(icons): add `.ti-icon.calendar` and `.ti-icon.schedule` icon variants with hover states
227
+ * feat(css): add `.ti-data-value.fill-space` modifier for flex-grow behavior in inline data value layouts
228
+ * fix(css): remove `min-width: 120px` constraint from `.ti-button.inline` for more flexible sizing
229
+ * fix(css): update z-index stacking values for dropdown and overlay elements to prevent layering conflicts
230
+ * build(deps): update `openid-client` from ^6.8.2 to ^6.8.4
231
+ * build(deps): update bundled `@alpinejs/csp` from ^3.15.11 to ^3.15.12
232
+ * build(deps): update bundled `htmx.org` from ^2.0.8 to ^2.0.10
233
+ * build(static): refresh bundled Alpine.js CSP and HTMX library files to match updated dependency versions
234
+
235
+ ## Version 1.6.2
236
+
237
+ * feat(ui): replace Material Symbols usage with framework-native `.ti-icon` classes across sidebar flyouts, login actions, and notification bar
238
+ * feat(sidebar): merge administration and user flyout menus into a single `sidebarApplicationMenu` with configurable menu icon and updated actions
239
+ * feat(css): add embedded SVG mask icon variants (`app-menu`, `dashboard`, `settings`, `error`, `user-profile`, `login`, `logout`, `internet`) and increase default icon size to `24px`
240
+ * refactor(theme): remove Material Symbols-specific icon styling from the black-glass theme
241
+ * build(static): remove external Google Material Symbols stylesheet import from static `index.html`
242
+ * build(release): bump package version from `1.6.1` to `1.6.2`
243
+
244
+ ## Version 1.6.1
245
+
246
+ * feat(css): add inline button support via `.ti-button.inline` and new `--ti-button-inline-height` design token
247
+ * refactor(ui): update sidebar flyout positioning logic to use shared `tiToolbox` viewport helpers (`getVisibleBox`, `clampToBox`)
248
+ * fix(ui): fix the call to utility functions `getVisibleBox` and `clampToBox` in the sidebar flyout component
249
+ * build(release): bump package version from `1.6.0` to `1.6.1`
250
+
251
+ ## Version 1.6.0
252
+
253
+ * feat(toolbox): add Alpine.js `tiToolbox` store with shared utility methods (`deepMerge`, `deepFreeze`, `structuredClone`, `formatDate`, viewport helpers, and cookie access)
254
+ * feat(ui): move sidebar menu configuration into `ti-framework.js` and register `tiComponentsConfig` during Alpine.js initialization
255
+ * refactor(static): remove legacy `ti-user-interface.js` from static assets and stop loading it from `index.html`
256
+ * refactor(components): update framework components to consume toolbox utilities through Alpine stores
257
+ * refactor(docs): add and expand JSDoc typedefs and method-level documentation in `ti-framework.js`
258
+ * build(release): bump package version from `1.5.3` to `1.6.0`
259
+
260
+ ## Version 1.5.3
261
+
262
+ * feat(framework): add `openScreen` method for in-app navigation
263
+ * fix(auth): add explicit HTTP `401` status to authentication failure
264
+
265
+ ## Version 1.5.2
266
+
267
+ * feat(framework): expand application API by improving `sendRequest`, `notify`, and `getLabel` methods
268
+ * feat(tooltip): add helper methods `getTooltipMessage`, `handleEnter`, `handleLeave`, `showTooltip`, `hideTooltip` to the tooltip component
269
+ * feat(css): improve styles and style structure
270
+
271
+ ## Version 1.5.1
272
+
273
+ * feat(framework): add `isValidDate` utility function for validating Date instances
274
+ * feat(framework): add `deepFreeze` utility function for recursive object freezing
275
+ * feat(framework): add `getLabel` method on application configuration for nested label resolution with dot notation
276
+ * feat(framework): add Alpine.js directive `x-text-label` for runtime label translation
277
+ * feat(framework): add a configuration object to replace labels object with enhanced structure
278
+ * feat(config): add authentication state (`auth.isAuthenticated`) to config endpoint response
279
+ * feat(placeholder): add inner content capture and injection for placeholder replacement
280
+ * feat(tooltip): add a new tooltip component with Alpine.js integration and positioning
281
+ * feat(css): add CSS custom properties for padding, margin, and font-family
282
+ * feat(css): add tooltip styling variables (background, foreground, size, shadow, arrow)
283
+ * feat(css): add `.ti-content.pane` block with flex layout and overflow handling
284
+ * feat(css): add error color, flyout item shadows, and separator color variables
285
+ * feat(css): add `.ti-glass-btn-black.large` variant with left-justified content
286
+ * refactor(framework): change user initialization from `undefined` to `null`
287
+ * refactor(framework): improve request failure handling with proper error rejection
288
+ * refactor(css): replace hard-coded spacing with CSS variables across components
289
+ * refactor(css): add `overflow: hidden` to body and `.ti-main` for better layout control
290
+ * refactor(css): convert color and styling values to CSS variables throughout
291
+ * refactor(handlers): add `convertUriToString` helper for safe URI object stringification
292
+ * refactor(handlers): add a request context object (query, params, headers, url, method) to JSON responses
293
+ * refactor(handlers): augment user data with default employeeID and roles in the user information handler
294
+ * build(deps): update express from ^5.1.0 to ^5.2.1
295
+ * build(deps): update express-session from ^1.18.2 to ^1.19.0
296
+ * build(deps): update lodash from ^4.17.21 to ^4.17.23
297
+ * build(deps): update openid-client from ^6.8.1 to ^6.8.2
298
+ * build(deps): update `@alpinejs/csp` from ^3.15.2 to ^3.15.8
299
+ * build(engines): update Node.js requirement from >=18.0.0 to >=20.0.0
300
+
301
+ ## Version 1.5.0
302
+
303
+ * feat(web-app)!: change `TiWebAppManager` to be an abstract class
304
+ * feat(web-app): rename class `WebAppManager` to `TiWebAppManager` and add a static file caching mechanism
305
+ * feat(web-app): add `addFragment` method with override protection for custom HTML fragment registration
306
+ * feat(web-app): add `webAppIdentifier` getter to expose application identifier
307
+ * feat(web-server): add support for dynamic web application instantiation from config via `classPath`
308
+ * feat(web-server): add `defineWebApplicationRoutes` and `defineUnprotectedRoutes` extension points
309
+ * feat(web-server): add `endpointEnabled` flag to conditionally enable API endpoint proxy
310
+ * feat(web-server): add serving for `.well-known` directory for web standards compliance
311
+ * feat(package): add public exports for `./web-application` and `./web-server` subpaths
312
+ * feat(package): add `files` whitelist and repository metadata (homepage, bugs URL, git repository)
313
+ * feat(package): add Node.js version requirement (>=18.0.0) via the `engines` field
314
+ * feat(build): add a post-install script to vendor HTMX and Alpine.js CSP libraries locally
315
+ * refactor(web-app): replace `fullPublicPath` with `staticContentPaths` array for multi-path static content resolution
316
+ * refactor(web-app): add file location search algorithm with caching via `#locateStaticFile` method
317
+ * refactor(web-app): update `transformHtml` signature to remove `fullPublicPath` parameter
318
+ * refactor(web-app): update `assembleHtmlView` to accept `staticContentPaths` instead of `fullPublicPath`
319
+ * refactor(web-server): replace the single static path with configurable `staticContentPaths` array
320
+ * refactor(web-server): merge web server default config with the provided service config in constructor
321
+ * refactor(web-server): load web server default config directly from the package JSON import instead of the ENV configuration
322
+ * refactor(web-server): improve TLS initialization error handling to reject promise instead of throw
323
+ * refactor(package): reorganize imports from `./server/...` to `./bin/...` and `./components/...` paths
324
+ * refactor(package): move `@alpinejs/csp` from dependencies to devDependencies
325
+ * build(static): replace CDN script references with local copies for HTMX and Alpine.js CSP
326
+ * build(env): remove `TI_INSTANCE_CONFIG` and update `TI_LOCALIZATION_LABELS_PATH` to use `bin/localization/` path
327
+ * build(env): add `TI_AUDITING_LOG_MIN_LEVEL` configuration variable
328
+ * fix(ui): change `aria-expanded` binding from string to boolean in the sidebar flyout component
329
+ * fix(ui): remove incorrect `type="button"` attribute from `Home` anchor element
330
+ * docs: improve various documentation comments and class descriptions
331
+
332
+ ## Version 1.4.0
333
+
334
+ * feat(ui): add a notification bar component with Alpine.js integration and auto-dismiss functionality
335
+ * feat(ui): add CSS variables and styles for the notification system
336
+ * feat(routes): add `/not-found` fragment and route for 404 error pages
337
+ * feat(routes): add `/app/error` route for error handling testing - will be removed later
338
+ * feat(routes): add `/app/config` data endpoint for serving application configuration
339
+ * feat(localization): add language property to `User` class with getter and JSON serialization
340
+ * feat(localization): integrate localization module for label management and localized error messages
341
+ * feat(session): add language property to session data populated from user or service configuration
342
+ * feat(handlers): add response type detection helper `isAcceptingResponseType` for content negotiation
343
+ * feat(handlers): enhance error responses with localized messages via localization module
344
+ * feat(handlers): add HTMX-aware error handling with HX-Redirect and HX-Retarget headers
345
+ * feat(handlers): implement `processDataRequest` method in WebAppManager for serving data resources
346
+ * feat(config): add a language configuration option to the `WebServiceConfiguration` object
347
+ * refactor(handlers): delegate error handling from resource protection to error middleware
348
+ * refactor(handlers): improve 401/404 response handling by routing through exceptions and middleware
349
+ * refactor(handlers): enhance CSRF and origin validation to use error middleware instead of direct responses
350
+ * refactor(handlers): improve service call error handling to raise exceptions and delegate to middleware
351
+ * refactor(handlers): refactor invalid route handler to raise exceptions instead of direct 404 responses
352
+ * fix(types): correct ExpressRequest typedef from `import("express").req` to `import("express").Request`
353
+ * build(config): update publicPath from `packages/web-framework/bin/static` to `bin/static`
354
+ * build(config): update TLS certificate paths to use relative `bin/tls/` paths
355
+ * build(env): update `TI_INSTANCE_CLASS` and `TI_INSTANCE_CONFIG` paths to use relative `bin/` paths
356
+ * build(env): add `TI_LOCALIZATION_LABELS_PATH` environment variable for custom labels
357
+ * build(run): update IDE run configuration to use relative paths and the correct working directory
358
+ * build(labels): add an empty `web-server-labels.json` file for custom localization labels
359
+
360
+ ## Version 1.3.0
361
+
362
+ * feat: first working prototype version