tekir

Changelog

Every tekir package release in chronological order. Each package also keeps its own changelog on its detail page.

CoreSeptember 16, 2026
  • Routing now applies domain constraints and precedence consistently on native Bun and Node servers, while compiled handlers preserve request, cookie, response, and error semantics.
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
CoreSeptember 16, 2026
  • Runtime adapters now provide portable Node fallbacks for server, filesystem, password, process, garbage-collection, and SQLite operations.
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
UtilitiesSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
UtilitiesSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
UtilitiesSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
UtilitiesSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
UtilitiesSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
UtilitiesSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
UtilitiesSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
UtilitiesSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
UtilitiesSeptember 16, 2026
  • Rendered responses no longer attach a body to status codes that forbid one.
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
@tekir/dbv0.1.8
DatabaseSeptember 16, 2026
  • Transactions no longer replay or erase concurrent writes during rollback, migrations apply atomically, and SQLite snapshots preserve schema objects and integer values safely.
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
DatabaseSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
DatabaseSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
DatabaseSeptember 16, 2026
  • HTTP caching now preserves binary bodies and response status, isolates host and credential variants, and refuses private, no-store, error, empty, and cookie-setting responses.
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
DatabaseSeptember 16, 2026
  • Session middleware and database storage now honor real Tekir response contexts and expiry semantics consistently.
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
SecuritySeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
SecuritySeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
SecuritySeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
SecuritySeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
SecuritySeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
SecuritySeptember 16, 2026
  • Shield and CSRF middleware now operate against real Tekir request/response contexts without producing integration-time 500 responses.
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
SecuritySeptember 16, 2026
  • Database-backed limits expire correctly and request identity no longer collapses unrelated client IPs into one quota.
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
SecuritySeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
SecuritySeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
CommunicationSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
CommunicationSeptember 16, 2026
  • Worker shutdown is idempotent under concurrent calls, and database/Redis backends retain consistent job state through claims, retries, and cleanup.
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
CommunicationSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
CommunicationSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
CommunicationSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
Storage & ParsingSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
Storage & ParsingSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
Storage & ParsingSeptember 16, 2026
  • Spilled uploads now load through Node-compatible ESM imports instead of a CommonJS-only runtime require.
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
DecoratorsSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
DecoratorsSeptember 16, 2026
  • The cache decorator now loads its optional cache integration through portable ESM resolution.
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
DecoratorsSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
DecoratorsSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
DecoratorsSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
DecoratorsSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
Dev ToolsSeptember 16, 2026
  • The scaffolder now uses the shared package build pipeline while preserving every starter template in the published output.
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
Dev ToolsSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
Dev ToolsSeptember 16, 2026
  • OpenAPI route collection now includes domain-constrained handlers.
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
Dev ToolsSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
FrontendSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
FrontendSeptember 16, 2026
  • Published output now uses the shared Node-targeted ESM bundle pipeline with external dependencies and generated TypeScript declarations, while Bun consumers keep the native source export.
CoreAugust 22, 2026
  • Graceful shutdown now stops accepting new connections and waits for active requests to drain before application hooks and providers close their resources. The public graceful flag is mapped to Bun's closeActiveConnections argument correctly, while immediate shutdown still force-closes active connections.
CoreAugust 22, 2026
  • The Node.js HTTP fallback now returns an awaitable shutdown promise, stops accepting new connections before cleanup and waits for active requests to finish. Forced shutdown also closes active Node.js connections immediately, matching the Bun server contract.
Dev ToolsJuly 31, 2026
  • ApiParamOptions.schema and fluent apiParam(..., { schema }) now preserve complete OpenAPI parameter schemas, including enums, arrays, bounds, unions, and other JSON Schema keywords.
  • Legacy parameter options continue to emit type, format, example, and enum correctly.
CoreJuly 23, 2026
  • Body-parser middleware now owns multipart consumption without an eager formData() pass, while request helpers read the parsed middleware result directly.
CoreJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
UtilitiesJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
UtilitiesJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
UtilitiesJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
UtilitiesJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
UtilitiesJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
UtilitiesJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
UtilitiesJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
UtilitiesJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
UtilitiesJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
@tekir/dbv0.1.7
DatabaseJuly 23, 2026
  • Optional PostgreSQL and MySQL drivers are resolved from the package module consistently, including combined and parallel test runs.
DatabaseJuly 23, 2026
  • Mongoose uses the package's resolved module instance consistently, including combined and parallel test runs.
DatabaseJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
DatabaseJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
DatabaseJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
SecurityJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
SecurityJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
SecurityJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
SecurityJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
SecurityJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
SecurityJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
SecurityJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
SecurityJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
SecurityJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
CommunicationJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
CommunicationJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
CommunicationJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
CommunicationJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
CommunicationJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
Storage & ParsingJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
Storage & ParsingJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
Storage & ParsingJuly 23, 2026
  • Multipart parsing now enforces a configurable maxParts ceiling in both streaming and fallback paths, rejecting excessive field-and-file payloads with 413 before they can exhaust parser resources.
DecoratorsJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
DecoratorsJuly 23, 2026
  • Cache decorators resolve their optional cache peer from the package module consistently instead of depending on the caller's runtime resolution base.
DecoratorsJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
DecoratorsJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
DecoratorsJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
DecoratorsJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
Dev ToolsJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
Dev ToolsJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
Dev ToolsJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
Dev ToolsJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by the coordinated Tekir release.
Dev ToolsJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
FrontendJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
FrontendJuly 23, 2026
  • Package metadata now follows the shared compatible 0.1.x dependency range used by this coordinated Tekir release.
CoreJuly 16, 2026
  • Compiled handlers now create real response state lazily whenever handlers or middleware consume ctx.response; cookies, headers, status codes, finish callbacks, streams, and wrapped native Responses are no longer silently dropped.
  • Compiled request contexts expose the complete request API, include a Cookie-header fallback, accept structured JSON MIME types, preserve multipart files, and reject dangerous query/input keys.
  • Compiled route and middleware failures now use the configured exception pipeline, while debug and trustedHosts settings are isolated per server instance. Plain-value routes remain on the lightweight fast path.
CoreJuly 16, 2026
  • The Node HTTP adapter streams response bodies instead of buffering them and preserves multiple Set-Cookie headers correctly across runtime boundaries.
UtilitiesJuly 16, 2026
  • Command discovery is confined against escaping symlinks, prompt retries create clean readline sessions, and command failures no longer leave dead control flow.
UtilitiesJuly 16, 2026
  • Deep configuration redaction now handles cycles, arrays, inherited properties, and falsy namespace values without leaking the original object.
UtilitiesJuly 16, 2026
  • Environment schema regression coverage now verifies defaults, coercion, choices, optional values, and invalid-input failures across the public API.
UtilitiesJuly 16, 2026
  • Accept-Language negotiation now honors quality weights, exclusions (q=0), wildcard fallback, and deterministic preference ordering.
UtilitiesJuly 16, 2026
  • Structured fields can no longer forge the selected severity, file transport failures are contained without unhandled rejections, and provider shutdown flushes transports reliably.
UtilitiesJuly 16, 2026
  • Datadog metadata can no longer override reserved status, service, or message fields in emitted log events.
UtilitiesJuly 16, 2026
  • Loki Basic authentication now supports Unicode credentials without failing during header encoding.
@tekir/dbv0.1.5
DatabaseJuly 16, 2026
  • orWhere conditions now participate in update, delete, increment, and decrement mutations, preventing an OR-only mutation from accidentally affecting every row.
  • Database CLI, transaction, model, and query-builder error paths now fail explicitly instead of silently continuing with partial state.
DatabaseJuly 16, 2026
  • Write/delete ID paths reject operator objects and invalid identifiers before reaching Mongoose, and fillable assignment ignores inherited properties.
DatabaseJuly 16, 2026
  • Distributed locks are released only by their owner through an atomic compare-and-delete operation, and connection/manager cleanup paths are more defensive.
DatabaseJuly 16, 2026
  • Regenerated session cookies are emitted through every supported response sink, store TTL semantics are consistent, and session/store errors no longer disappear silently.
SecurityJuly 16, 2026
  • Database-token authentication now persists and verifies APP_KEY-keyed HMACs consistently, updates token usage safely, and keeps authentication middleware failures explicit.
SecurityJuly 16, 2026
  • Scrypt verification now rejects malformed, oversized, and attacker-controlled cost parameters before allocating memory or starting expensive work.
SecurityJuly 16, 2026
  • Legacy ciphertext whose IV starts with the version byte is decoded correctly, and APP_KEY environment fallback no longer assumes a Node-style global process.
SecurityJuly 16, 2026
  • CSRF exception matching now respects path boundaries, preventing a configured path such as /api/public from excluding attacker-chosen prefix lookalikes.
SecurityJuly 16, 2026
  • Redis rate limiting uses an atomic Lua consume operation for increment, TTL, and lockout decisions; memory-store cleanup and observation paths are bounded and consistent.
SecurityJuly 16, 2026
  • Validation middleware supports safeParse and safeParseAsync schemas and commits transformed request data only after the full validation succeeds.
SecurityJuly 16, 2026
  • OAuth state validation rejects future and malformed timestamps, production requires a signing key, and provider token/user parsing is stricter and safer.
CommunicationJuly 16, 2026
  • Resend and Sevk transports normalize trailing slashes in custom API base URLs, preventing malformed double-slash endpoints.
CommunicationJuly 16, 2026
  • Redis jobs use atomic claim and lease recovery, worker polling survives backend errors, repeated stop calls settle safely, and memory/database backends retain completed records consistently.
CommunicationJuly 16, 2026
  • Notification delivery isolates channel/user failures, applies configured default channels, supports both database adapter shapes, injects mail correctly, and deeply redacts persisted sensitive data.
CommunicationJuly 16, 2026
  • Async listener dispatch now awaits Promise-compatible thenables and cleans abort listeners without leaking wait subscriptions.
CommunicationJuly 16, 2026
  • Cron overlap protection and error tracking now recognize Promise-compatible thenables, not only native Promise instances.
Storage & ParsingJuly 16, 2026
  • S3 SigV4 now signs query parameters and encoded object keys correctly, remote LIST/DELETE failures are surfaced, same-key moves are safe, and local/memory drivers close symlink and mutable-buffer escapes.
Dev ToolsJuly 16, 2026
  • Starter templates now use credential-safe CORS defaults, keep frontend dependency versions aligned, and reject unsafe project targets before scaffolding.
Dev ToolsJuly 16, 2026
  • Test applications await startup and shut down after migration failures, explicit migrations require @tekir/db, setup errors are surfaced, and authenticated clients retain the head() method.
Dev ToolsJuly 16, 2026
  • Memory health checks retain two-decimal precision so small but valid heap/RSS measurements are no longer reported as zero.
FrontendJuly 16, 2026
  • Build embedding and production asset serving reject symlink escapes outside configured roots, including paths that pass lexical containment checks.
FrontendJuly 16, 2026
  • The internal Next server and app now close during Tekir shutdown, listener startup errors clean up partial state, and a later request can retry initialization.
CommunicationJuly 2, 2026
  • Jobs can now run in a fixed IANA timezone: new Cron({ timezone: 'UTC' }), cron.setTimezone('UTC') before registering, or a per-job cron.add(name, pattern, cb, { timezone }). Patterns then evaluate in that zone instead of the host's local time, so 5 0 1 * *-style boundaries line up with UTC-based date math regardless of the server's timezone. Omitting it keeps the previous local-time behavior.
  • Found and fixed with Fable.
CoreJune 13, 2026
  • The compiled route fast path no longer rebuilds handlers from their source text at startup. Every route now calls your real handler closure directly, so handlers keep working unchanged after a minifier or transpiler rewrites their source, and a whole class of source-reparse edge cases is gone.
  • Request body size is now capped by default (10 MB, from bodyParser.maxSize) on both the Bun and Node paths. Oversized requests get a 413 before the handler runs instead of being buffered into memory.
  • Query strings and request helpers (input, all, only, except) now reject __proto__, constructor, and prototype keys and build their output on null-prototype objects, closing a prototype pollution vector.
  • Response headers carrying CR/LF (including Vary) are now dropped, closing a response-splitting surface. Invalid percent-encoding in route params and wildcards no longer throws; the raw segment is used instead.
  • response.redirect.back() now validates the referer against an optional trustedHosts allowlist (exact host plus *.subdomain wildcards) and otherwise keeps only the same-origin pathname + search. A path that registered specific methods now answers unmatched methods with a 405 Allow response and routes OPTIONS through the global middleware chain.
  • generate:key no longer prints the generated APP_KEY to stdout. Signed cookie verification is unified on a single constant-time reader, and graceful SIGINT/SIGTERM shutdown now runs in every mode.
  • Found and fixed with Fable.
CoreJune 13, 2026
  • The Node server now streams response bodies chunk by chunk (with backpressure and client-disconnect aborts) instead of buffering the whole body into memory first.
  • serve() gained maxRequestBodySize (default 10 MB) and idleTimeout (default 120 s) on both runtimes. On Node, oversize requests are rejected with a 413 during accumulation, and request/header timeouts are set.
  • fileResponse() accepts an optional baseDir and rejects ../ escapes; MIME types are now detected consistently on both runtimes. openDatabase() skips WAL on read-only opens.
  • readFile/readFileText now produce a consistent error carrying code: 'ENOENT' and the path on both runtimes. Runtime detection now verifies process.versions.node and throws a clear error when neither Bun nor Node is detected.
  • Found and fixed with Fable.
UtilitiesJune 13, 2026
  • Command discovery now resolves the target directory to an absolute base and verifies every file stays under it, so a .. segment or symlink cannot load code from outside the intended folder. Import failures are surfaced with console.error instead of being swallowed.
  • Parsed flags and args are built on null-prototype objects and reject __proto__/constructor/prototype definition keys, closing a prototype pollution vector.
  • Negative numbers like -5 and -0.5 are now accepted as flag values; real flags are still rejected. An invalid choice prompt answer re-prompts with a clear message instead of silently falling back to the first option.
  • Ctrl+C in a prompt now restores raw mode and exits with code 130, and the secure prompt no longer double-consumes stdin.
  • Found and fixed with Fable.
UtilitiesJune 13, 2026
  • getAll() now redacts sensitive keys by default. Values under keys like password, secret, token, apiKey, privateKey, credential, and dsn (case-insensitive, at any depth) come back as [REDACTED]. Pass getAll({ redact: false }) to opt back into raw values; the store itself is never mutated.
  • register(name, value, schema?) accepts an optional validator and throws at register time when the value fails it, so a bad config object surfaces immediately instead of later at read time.
  • get() now rejects __proto__, constructor, and prototype path segments and returns the default value, closing a prototype pollution vector. Config files load through file:// URLs so drive letters and UNC paths resolve correctly on Windows.
  • loadDir import failures are now reported with the file name instead of being swallowed.
  • Found and fixed with Fable.
UtilitiesJune 13, 2026
  • defineEnv(schema, options?) now accepts envalid's CleanOptions, including a custom reporter, so you can throw on invalid env instead of having the process exit.
  • The return type now uses envalid's official CleanedEnv<T> inference, and the schema type is tied to ValidatorSpec, so invalid validator objects are caught at compile time.
  • Found and fixed with Fable.
UtilitiesJune 13, 2026
  • Locale loading and t() lookups now reject __proto__, constructor, and prototype keys and build their maps on null-prototype objects, closing a prototype pollution vector.
  • Locale files are now confirmed to resolve to a file under the locale directory, so a symlink or separator trick cannot pull translations from outside the tree.
  • t() output is not HTML-escaped; the docs now state explicitly that callers must escape translated strings before rendering them into HTML.
  • Found and fixed with Fable.
UtilitiesJune 13, 2026
  • Pretty-printed output now strips CR/LF, tabs, ANSI escapes, and control characters from logged strings, closing a log-injection and terminal-escape surface.
  • Field redaction is now recursive. Matching keys are redacted at every depth across nested objects, arrays, and the merged context; the caller's object is never mutated and circular references are handled.
  • The file transport gained a maxQueueSize (default 10000, drop-oldest with a dropped-line counter) so a slow disk cannot grow the write queue without bound.
  • Found and fixed with Fable.
UtilitiesJune 13, 2026
  • Added a maxBufferSize (default 10000) so a stalled intake cannot grow the buffer without bound; the oldest entries are dropped and a dropped counter is kept.
  • Failed deliveries are now visible. Non-2xx responses (for example a 403 from an invalid API key) and network errors increment an error counter and fire an optional onError(err) callback instead of being silent.
  • The flush timer is unref'd so it no longer keeps the process alive on shutdown.
  • Found and fixed with Fable.
UtilitiesJune 13, 2026
  • The Loki host is now validated to close an SSRF surface. Only http/https is allowed, and loopback, private, link-local, and cloud-metadata hosts are rejected by default. Set allowInsecureHost: true to opt into local/dev targets. Behavior change: localhost now requires allowInsecureHost.
  • When auth is configured, an http:// target is rejected without allowInsecureHost so credentials are not sent in plaintext.
  • Added a maxBufferSize (default 10000, drop-oldest with a counter) and res.ok checking, so a stalled endpoint cannot grow the buffer without bound and failed deliveries surface via an error counter and optional onError(err). The flush timer is unref'd.
  • Found and fixed with Fable.
UtilitiesJune 13, 2026
  • The bridge now checks that the selected Pino level method is actually a function before calling it, falling back to info (or returning quietly) instead of crashing at runtime.
  • Added a typed PinoLike interface so the pino config and transport field are no longer any.
  • Found and fixed with Fable.
UtilitiesJune 13, 2026
  • render() now sets X-Content-Type-Options: nosniff by default; callers can override it via headers.
  • The ViewEngine interface now documents the security contract that engine output must return escaped HTML.
  • Found and fixed with Fable.
@tekir/dbv0.1.4
DatabaseJune 13, 2026
  • Database TLS verification is now on by default. When SSL is in play and no explicit object is given, the driver applies { rejectUnauthorized: true }; turning verification off now requires an explicit { rejectUnauthorized: false }. The ssl config also accepts an optional ca.
  • Every SQL identifier (table and column names in createTable/dropTable/renameColumn and foreign keys) is now validated against a strict allowlist, so quote/backtick escape attempts throw Invalid SQL identifier instead of splicing into the query. Model aggregates (sum/avg/min/max/increment/decrement) now validate the column against the model schema.
  • transaction() now runs a real BEGIN/COMMIT/ROLLBACK against a single dedicated connection for Postgres, MySQL, and SQLite, so queries inside the callback are genuinely atomic and roll back together on error. Migrations run all their DDL statements inside a transaction where the engine supports it.
  • Connection pools now apply sane defaults (max/idle/connection timeouts) and the Postgres pool attaches an error handler so an idle-client error cannot crash the process. Driver connection errors are masked so the connection string password is no longer leaked into error messages.
  • Pagination clamps page/perPage to safe bounds (no negative OFFSET, no zero/NaN LIMIT).
  • Found and fixed with Fable.
DatabaseJune 13, 2026
  • Query filters are now sanitized against NoSQL injection. find, findOne, count, exists, deleteMany, updateMany, distinct, paginate, and the soft-delete scopes strip operator keys (those starting with $ or containing .) at every depth, while preserving Date/ObjectId and hand-built queries.
  • Update operations are now guarded: plain field maps are wrapped in $set, and explicit operator documents keep only a safe allowlist, so $rename/$unset/$where-style operators cannot be smuggled through.
  • findById returns null for non-string, object, or invalid ObjectId ids instead of throwing a CastError, so findOrFail gives a consistent not-found result for crafted ids.
  • Connections now apply pool-size and timeout defaults and attach an error handler (errors surface as a tekir:error event instead of an unhandled rejection). Query debug logging is off unless debug: true is set, so filter values are not logged by default.
  • Found and fixed with Fable.
DatabaseJune 13, 2026
  • New clearPrefix() deletes only this connection's <prefix>:* keys (and deletes nothing when no prefix is set). flushdb() is now flagged as dangerous and points at the safer alternative.
  • remember() now takes a short-lived SET NX EX lock so concurrent callers wait for one computation instead of stampeding, and setJSON() with a TTL uses a single atomic SET ... EX so a crash can no longer leave a key without its TTL.
  • Connecting over plaintext in production now logs a warning, and credentials in the connection URL are masked in logs.
  • getJSON parse failures now log a warning with the key name (return stays null), and send()/subscribe() document that they are advanced and that incoming messages must be treated as untrusted.
  • Found and fixed with Fable.
DatabaseJune 13, 2026
  • The HTTP response cache is now secure by default. Requests carrying Authorization or Cookie skip the cache entirely (authenticated: 'bypass'). Set authenticated: 'vary' to include credential headers in the cache key, or 'allow' to opt back into the previous behavior; a custom key builder is always honored. Behavior change: authenticated GETs are no longer cached by default.
  • getOrSet now shares a single in-flight computation across concurrent misses for the same key (single-flight), so a cold key under load runs factory() once instead of once per request.
  • RedisCacheStore.flush() now deletes only this store's keys via SCAN+DEL over <prefix>* instead of FLUSHDB, and throws on an empty prefix (which would have wiped the whole database). set uses an atomic SET ... EX where the client supports it.
  • The memory store now enforces a maxEntries cap (default 10000, FIFO eviction) with periodic sweeping of expired entries, and both stores gained a manual prune().
  • Found and fixed with Fable.
DatabaseJune 13, 2026
  • Session cookies are now HttpOnly + SameSite=Lax + Secure by default. Secure defaults to true in production (and when NODE_ENV is unset); set cookie.secure: false to opt out explicitly.
  • A regenerated session ID is now emitted reliably through whichever response sink is available, and it is an error to regenerate when no sink exists, so a new ID can never be silently dropped.
  • put/flash now reject __proto__, constructor, and prototype keys, closing a prototype pollution vector.
  • The memory store now evicts expired and over-cap entries via a periodic sweep (default 60 s, unref'd) and a maxEntries bound (default 100k), with a stop() for clean shutdown. Touching an existing session re-syncs its store TTL with the re-sent cookie Max-Age.
  • Found and fixed with Fable.
SecurityJune 13, 2026
  • JWT verification now enforces the algorithm. The header is decoded before the signature check and alg must be HS256 (and typ, if present, must be JWT), so alg:none and HS/RS confusion attacks are rejected. Tokens must also carry a finite exp and a sub, and nbf is honored when present.
  • Database token guard now requires APP_KEY and stores keyed-HMAC tokens. Tokens are persisted as HMAC-SHA256(token, APP_KEY) instead of a plain SHA-256 hash, the plaintext token is returned to the client only once and never written to the database, and a database leak can no longer be used to forge or replay tokens without APP_KEY. Existing stored tokens are invalidated and must be reissued.
  • Database token comparison now finishes in constant time, and an expired or unparseable expires_at is rejected rather than treated as a token that never expires.
  • Failed multi-guard authentication now returns a constant Unauthorized to the client; the guard-specific reason is only logged, so the response no longer reveals which guard failed.
  • Found and fixed with Fable.
SecurityJune 13, 2026
  • before-hook semantics are now fail-safe and per-ability. A hook only decides the ability it returns a strict true/false or AuthorizationResponse for; an accidental truthy non-boolean is no longer coerced into a global deny, so a hook can no longer lock down the whole system by mistake.
  • can(ability, resolver) now accepts a lazy (ctx) => unknown[] resolver, so the resource can be loaded from the request and ownership/IDOR checks can run in the middleware. The static-args form stays backward compatible.
  • can() now denies when auth.isAuthenticated === false even if a user object is present, falling back to user presence only for older adapters that never set the flag.
  • Found and fixed with Fable.
SecurityJune 13, 2026
  • verify() now only returns false for an unrecognized or malformed hash. Real runtime/infrastructure failures (a missing native module, OOM, and similar) are thrown instead of being swallowed into a silent false, which previously could mask a broken setup as a wrong password.
  • bcrypt make()/verify() warn when the input exceeds bcrypt's 72-byte limit (where the tail is silently ignored), pointing at pre-hashing or argon2/scrypt.
  • scrypt verification validates the parsed N,r,p,keylen parameters and returns false on invalid values without calling into crypto.scrypt.
  • Found and fixed with Fable.
SecurityJune 13, 2026
  • Each encryption now generates a fresh 16-byte random salt and embeds it in the payload, with key derivation bound to that salt. The same APP_KEY produces a different key per ciphertext, closing precompute/rainbow attacks and key sharing across installs.
  • The constructor now validates APP_KEY for a minimum length and basic entropy and throws a clear error otherwise.
  • A decrypt JSON-parse failure now throws the same generic error as a decryption failure (the JSON hint is log-only), so it does not signal payload structure to an attacker.
  • Found and fixed with Fable.
SecurityJune 13, 2026
  • CORS now requires an explicit origin allowlist when credentials are enabled. Combining credentials: true with origin: true (reflect any origin) now throws at construction; you must pass a concrete string, array, or function.
  • With credentials enabled, an Origin: null request is now passed through without CORS headers instead of echoing null, and Access-Control-Allow-Headers reflects only the headers the client actually asked for rather than * (the * behavior is kept when credentials are off).
  • Array origin matching is now exact and case-sensitive (the previous lowercasing is gone), and empty methods/headers no longer emit an empty Allow-* header.
  • Found and fixed with Fable.
SecurityJune 13, 2026
  • CSRF tokens are now HMAC-signed when a secret is set. The session stores only the random value and verification recomputes the HMAC and compares in constant time. Verification is now fail-closed: a missing token is rejected instead of being lazily minted as valid.
  • Added rotateCsrfToken(ctx) (call it after login/logout) plus csrf({ rotateOnUse: true }) for one-time rotation after each successful mutation.
  • shield() now applies CSP defaults even when csp is not specified; pass csp: false to disable it. X-Frame-Options is restricted to DENY/SAMEORIGIN (the deprecated ALLOW-FROM is removed), and HSTS preload now defaults to false (opt-in).
  • Found and fixed with Fable.
SecurityJune 13, 2026
  • The rate limiter no longer trusts X-Forwarded-For without trustProxy. The new trustProxy option defaults to false, in which case only ctx.request.ip is used; set it to true (left-most) or a number of hops to parse the forwarded chain, so a client can no longer spoof its identity to dodge limits.
  • Counting is now atomic at the store level. The check-then-consume race is gone (a single atomic consume handles increment plus lockout), verified under concurrency, so a burst of simultaneous requests can never exceed the limit.
  • Identifiers are URL-encoded before building the bucket key, so a : in an IPv6 or custom identifier cannot collide with another bucket. The memory store now sweeps expired entries on a periodic, unref'd timer.
  • Retry-After is now sent only when the limit is actually exceeded.
  • Found and fixed with Fable.
SecurityJune 13, 2026
  • Validated data is now written to the context only after every source has passed, so a failure on one source can no longer leave a partially-validated request in place.
  • An unrecognized schema shape now throws (fail-closed) instead of silently passing the request through unvalidated.
  • Valibot schema detection is now reliable and ordered ahead of Zod, fixing a dead branch.
  • Found and fixed with Fable.
SecurityJune 13, 2026
  • Apple id_token is now cryptographically verified. The token's RS256 signature is checked against Apple's JWKS (with the kid, cached for an hour) and iss/aud/exp (plus optional nonce) are validated, so a forged or decode-only token is rejected and only a verified email_verified address is accepted.
  • OAuth flows now use PKCE (S256) end to end. Every redirect() generates a verifier/challenge, the authorization URL carries code_challenge, and exchangeCode sends the code_verifier.
  • OAuth state is now bound to the user's session. handleCallback requires the stored state in both signed and plain modes (fail-closed) and compares the state's bound nonce in constant time, closing login-CSRF and replay.
  • Redirect validation now allows only http(s) (rejecting javascript:/data:/file:), enforces real label boundaries for wildcard matches, and requires HTTPS. The GitHub email fallback accepts only a primary verified address. Access and refresh tokens are made non-enumerable so they do not leak through JSON.stringify, spread, or most logging.
  • Found and fixed with Fable.
CommunicationJune 13, 2026
  • Every message is now sanitized for header injection before it reaches a transport. from/to/cc/bcc/replyTo/subject, custom headers, and attachment filenames are stripped of CR/LF, including for messages dispatched directly or via the notification mail channel that previously bypassed the builder.
  • SMTP now enforces TLS. requireTLS defaults to true on non-secure connections (STARTTLS required) and tls defaults to verifying the certificate (rejectUnauthorized: true), with optional ca/servername.
  • The log transport now redacts by default (LogConfig.redact, default true): addresses are masked, the body is never logged, and non-pretty mode writes only metadata. Set redact: false to opt back into full content.
  • Provider error bodies returned to the caller are truncated, and the SES transport's signing date and canonical query are now deterministic and RFC-3986 correct.
  • Found and fixed with Fable.
CommunicationJune 13, 2026
  • Job claiming is now atomic on the database and Redis backends, so two workers can never pick up the same job. The database backend uses a conditional claim with a unique token; the Redis backend uses a single Lua script, which also means a crash mid-claim no longer strands a job.
  • Both backends gained a visibility timeout (60 s). A job whose worker died mid-processing is recovered back to pending instead of being lost, and retries now persist the attempt count on the existing row instead of re-pushing (no primary-key collisions).
  • Poison jobs no longer retry forever. A new NonRetryableError is thrown for invalid JSON or an unregistered job class and fails the job immediately without consuming the retry budget.
  • concurrency(n) and pollInterval(ms) now reject non-positive, NaN, and negative values.
  • Found and fixed with Fable.
CommunicationJune 13, 2026
  • The database channel now redacts sensitive keys (password, token, secret, apiKey, otp, pin, ssn, cvv, and similar, including nested objects) before storing a notification payload.
  • FCM push now sends a matching auth and endpoint pair: an OAuth access token uses the v1 endpoint with Authorization: Bearer, otherwise the legacy key= endpoint is used, and both paths check response.ok and honor a configured endpoint. User ids are normalized to FCM's allowed topic character set.
  • send and sendMany now use Promise.allSettled, so one channel or recipient failing no longer drops the others; failures are logged.
  • The mail channel sanitizes its payload through @tekir/mail's header-injection sanitizer as defense in depth.
  • Found and fixed with Fable.
CommunicationJune 13, 2026
  • A handler that throws no longer aborts dispatch or rejects the emit. A shared error path routes the error to onError (or console.error) and continues to the remaining handlers, including wildcard onAny handlers, which previously swallowed errors silently. Behavior change: with no onError set, emit no longer rejects on a handler error (handlers are isolated by default).
  • wait() and the async-iterator helpers no longer leak listeners. Timeout, abort, and normal completion all remove the handler and clear timers, so repeated timeouts or a break out of for await cannot accumulate listeners.
  • The async-iterator buffer is now bounded by maxBufferSize (default 1024, drop-oldest), so a fast producer with a slow or absent consumer cannot grow memory without bound.
  • Added a per-event listener threshold (default 100) with a one-time possible-memory-leak warning and a setMaxListeners(n) API (0 disables it).
  • Found and fixed with Fable.
CommunicationJune 13, 2026
  • Overlapping runs of the same job are now prevented. A per-job in-flight flag (plus the underlying scheduler's overlap protection) skips a tick that arrives while the previous async run is still going, so a long job no longer runs concurrently with itself.
  • An invalid cron pattern now throws at registration time with the job name and pattern, and the job is not registered, instead of failing later at tick time.
  • Added an async shutdown() that stops every job's timer and clears the registry, so no further ticks fire after it returns.
  • Found and fixed with Fable.
Storage & ParsingJune 13, 2026
  • New serveDrive() fallback handler that, by default (requireSignature: true), requires a valid token+expires signature on every request under its URL prefix and returns 403 for a missing, wrong, or expired signature.
  • LocalDriver now enforces upload validation. A new upload option (allowed extensions plus max size, settable per disk in config/drive.ts) is applied in put(), including the streaming path, and helpers like sanitizeFilename/validateUpload are exported.
  • Local path handling is hardened: a key containing a null byte throws Path traversal detected, and getUrl/getSignedUrl resolve and per-segment URL-encode the key while keeping the signature round-trip intact.
  • The S3 list() now follows pagination to return every key and decodes XML entities in key names.
  • Found and fixed with Fable.
Storage & ParsingJune 13, 2026
  • A decoded path containing a null byte is now rejected as malformed instead of reaching the filesystem.
  • Symlink traversal is now blocked when opted in. With symlinks: 'deny', the resolved real path is verified to stay under the root in both the middleware and the provider fallback (default stays 'follow' for backward compatibility).
  • A read error mid-request now falls through to next() instead of crashing, and the docs call out that dotFiles: 'allow' will serve .env/.git.
  • Found and fixed with Fable.
Storage & ParsingJune 13, 2026
  • Multipart parsing is now streaming with limits applied as the body is read. maxFileSize, total limit, maxFiles (default 20), maxFields (default 1000), and maxParts (default 1000) are enforced during the read and abort early with a 413 once exceeded, so a large upload is no longer buffered fully into memory. Parts over spillThreshold (default 1 MB) stream to a temp file to keep memory bounded.
  • JSON, form, and raw bodies are also size-limited during the read (with a Content-Length pre-check) and cancel as soon as the limit is exceeded.
  • JSON parsing now strips __proto__, constructor, and prototype keys recursively, consistent with the urlencoded path, closing a prototype pollution vector.
  • SVG content detection is hardened so that crafted XML/HTML no longer passes as an image; the docs note that SVG is an active document and should not be added to an extnames whitelist without sanitization. Method spoofing is now opt-in via methodSpoofing: true and only upgrades real POST requests (a GET can never be mutated).
  • Found and fixed with Fable.
DecoratorsJune 13, 2026
  • createEventDecorator now handles instance methods instead of silently doing nothing: an instance method is registered against its constructor with a bound handler.
  • compose now reverses a copy of the decorator list, so applying the same composed decorator to multiple classes is order-consistent (idempotent).
  • Found and fixed with Fable.
DecoratorsJune 13, 2026
  • Controller loading is now resilient. A controller whose constructor throws is skipped with a named warning instead of aborting registration, a method that is not actually a function is skipped, and the remaining routes still register.
  • A subclass no longer shares its parent's __routes array; it inherits a copy, so adding routes on a subclass cannot mutate the parent.
  • Importing @tekir/http-decorators no longer pulls in @tekir/cache; the cache dependency is loaded lazily only when @Cache(...) is actually used, with a clear error if the package is missing. @Websocket routes now warn that they are skipped instead of disappearing silently.
  • Found and fixed with Fable.
DecoratorsJune 13, 2026
  • Field decorators (@hidden, @fillable, @cast, relations) and lifecycle hooks now build their own collection per class and inherit a copy of the parent's, so adding fields or hooks on a subclass no longer mutates the parent class. This keeps the mass-assignment boundary (@hidden/@fillable) correct across inheritance.
  • Found and fixed with Fable.
DecoratorsJune 13, 2026
  • @Schedule now validates the cron pattern at decoration time, throwing a meaningful error (with the pattern) for an empty pattern or a wrong field count.
  • @Every now enforces sensible ranges (1-59 for seconds/minutes, 1-23 for hours) and rejects out-of-range or unrecognized values like 90s, 25h, or 1d instead of silently falling back.
  • @CronJob now walks the prototype chain and reads methods without triggering getters, collecting each overridden method once.
  • Found and fixed with Fable.
DecoratorsJune 13, 2026
  • Multiple event decorators on one method are now all registered. Stacking @On('a') @On('b') (or @On plus @Once) on the same method binds each one separately instead of the last one winning.
  • @On/@Once now throw at decoration time for an empty or whitespace-only event name.
  • @Listener now walks the prototype chain and reads methods without triggering getters, collecting each overridden method once.
  • Found and fixed with Fable.
DecoratorsJune 13, 2026
  • Added tests that guard the public API surface: every documented decorator is verified to be defined and each re-exported symbol is confirmed to be the same reference as its original in @tekir/swagger, so an export drift cannot slip through unnoticed.
  • Found and fixed with Fable.
Dev ToolsJune 13, 2026
  • The entry path is now validated to stay under the current working directory before it is imported, so a crafted --entry ../../evil.ts (or an absolute path outside the repo) is refused rather than executed. Both the run and build paths share this guard.
  • Env-file loading no longer double-applies. After the parent loads env files it marks them so the re-exec'd watch child inherits the flag and skips reloading, removing duplicate loads and warnings. Quoted env values are taken verbatim and an unquoted trailing # comment is trimmed without corrupting tokens like pa#ss.
  • Writing the temporary build entry is now wrapped so a read-only directory produces a clear warning and falls back to a full-entry import instead of crashing.
  • On Node, tekir test uses npx --no-install vitest run, so a missing local vitest fails fast instead of silently downloading from the registry.
  • Found and fixed with Fable.
Dev ToolsJune 13, 2026
  • The project name passed on the command line is now validated. A target that resolves outside the current directory (via ./.., an absolute path, a path separator, or other unexpected characters) is rejected before any directory is created, so scaffolding cannot write to an arbitrary location.
  • The generated package.json name is normalized (lowercased, leading ./_ stripped, falling back to app), so an unusual project name no longer produces an invalid manifest.
  • Interactive prompts now handle a closed or piped stdin (CI) by resolving empty instead of hanging forever.
  • Found and fixed with Fable.
Dev ToolsJune 13, 2026
  • Table names in test database helpers are now validated and any embedded quote is escaped, so a quoted table name cannot break the query.
  • FakeDate now forwards its constructor arguments unchanged, so new Date(timestampNumber) parses correctly instead of producing an Invalid Date.
  • The in-memory database override now uses a shallow structural clone (only swapping the sqlite path to :memory:) instead of a JSON round-trip, so function/Date/undefined fields in the config survive.
  • JSON assertions (assertJson/assertJsonContains/assertJsonPath/assertError) now compare structurally and are independent of key order.
  • Found and fixed with Fable.
Dev ToolsJune 13, 2026
  • Swagger docs are now gated by environment by default. Under NODE_ENV=production with no auth configured, the routes are not registered and a warning is logged. Use SwaggerConfig.enabled to force the docs on or off in either direction.
  • New @ApiHide() decorator plus SwaggerConfig.hidePaths (string prefix or RegExp) let you keep internal routes out of the generated spec.
  • Found and fixed with Fable.
Dev ToolsJune 13, 2026
  • Debug info is no longer included in the report by default. Pass run({ debug: true }) (from an internal or authorized endpoint) to include it. Behavior change: the public report no longer exposes pid, platform, or version.
  • Each check now runs under a timeout (default 5000 ms, configurable via run({ timeout })); a timed-out or throwing check is reported as an error result instead of taking down the whole report.
  • DbCheck/RedisCheck now return a generic Connection failed; the raw error is log-only, so connection details are not leaked in the response. A report with no checks registered now warns rather than reporting a misleading healthy.
  • Found and fixed with Fable.
FrontendJune 13, 2026
  • Static file serving for public/ and dist/ now goes through a hardened path resolver, matching @tekir/static: percent-decoding is guarded, null bytes and cross-drive paths are rejected, every segment is checked for dotfiles (.env/.git), and backslash-encoded separators are handled. This closes a path-traversal surface in the prod static fallback.
  • The compiled import path is now embedded safely so a crafted path cannot break out of the generated source.
  • Found and fixed with Fable.
FrontendJune 13, 2026
  • Proxied responses now stream straight through instead of being buffered into memory first, so large or streaming Next responses no longer pay a full-body buffering cost.
  • A failed hop into the internal Next listener now returns 502 Bad Gateway and is logged; a 404 only comes from Next's own response. If Next fails to start, the init state is reset so the next request retries instead of staying broken.
  • Found and fixed with Fable.
CoreMay 28, 2026
  • The server now binds the configured host. Setting host in config/app.ts (or tekir({ config: { app: { host } } })) is honoured instead of always listening on every interface. Previously the value was read for the Node fallback runtime but never passed to Bun.serve, so on Bun the bind address silently stayed 0.0.0.0. Use host: '127.0.0.1' to accept only local connections, host: process.env.HOST ?? '0.0.0.0' to wire it from the environment, or leave it unset to keep the default 0.0.0.0 (all interfaces). hostname is accepted as an alias for host (matching Bun.serve's option name); host wins if both are set.
CoreMay 28, 2026
  • Fixed: async function* route handlers streamed nothing and returned {} with a JSON content type. Async generators expose Symbol.asyncIterator rather than Symbol.iterator, and the handler dispatcher only recognised the latter, so the generator object fell through to JSON serialisation (which has no enumerable keys). Both the middleware and no-middleware dispatch paths now detect either iterator protocol. Sync function* handlers on a route with no middleware were also affected — that path had no generator detection at all — and now stream correctly too.
  • Fixed: streamed responses always went out as Content-Type: text/plain even when the handler yielded SSE frames (data: ...). The SSE-vs-plain decision ran inside the stream's pull() callback, but new Response(stream, init) snapshots its headers at construction, before pull() ever fires, so the detection was dead code. The first chunk is now pulled up front, so an SSE generator correctly emits Content-Type: text/event-stream (plus X-Accel-Buffering: no so proxies do not buffer the stream). Native EventSource clients work against generator routes now; previously only manual fetch().body.getReader() consumers did. Object streams that are not SSE keep their newline-delimited text/plain JSON shape.
CoreMay 17, 2026
  • Inline routes that return a fully static literal (() => ({ message: 'Hello' }), () => [1, 2, 3], primitives, deeply-nested literal objects, etc.) now serialise their body once at registration time and emit a new Response(precomputedString, frozenInit) per request. Routes that touch params, query, body, or any closure variable keep going through the existing compiled path so behaviour is unchanged; only handlers whose entire return expression is JSON-safe and free of identifiers like new, function, this, globalThis, Bun, etc. opt in.
  • The synthetic 404 fallback now lives under Bun.serve's native /* route instead of going through the fetch callback when the server has no WebSocket routes, no domain routes, and no user-supplied server.fallback(...). In that case serveConfig.fetch is dropped entirely, so unmatched requests dispatch through the radix tree without a JS callback round trip. Apps that use websockets, multi-tenant subdomain routing, or call server.fallback(handler) keep the previous behaviour.
  • Shared JSON_RESPONSE_INIT constant used by every JSON response path (compiled handlers, response.json(), response.send(obj), response.ok(obj), response.created(obj), etc.) so the per-request header allocation drops out of the hot path. Functionally identical to the previous Response.json(...) call; the wire format does not change.
CoreMay 17, 2026
  • response.encryptedCookie(name, value, secret) now produces an authenticated AES-256-GCM ciphertext instead of a base64-encoded payload with an HMAC tag. Anyone observing the cookie value can no longer decode its contents; tampering fails the auth tag check on the read side. The cookie shape changes from ${base64url}.${signature} to ${iv}.${ciphertext}.${authTag}; cookies issued by older releases will not decrypt with the new reader and need to be re-issued (clear and let the next request mint a fresh one).
  • New top-level helpers encryptCookieValue(value, secret), decryptCookieValue<T>(token, secret), and verifySignedCookieValue(token, secret) exported from @tekir/core. Use them on the request side to read back cookies set via response.encryptedCookie(...) and response.signedCookie(...). Both readers return null on tampering, expiry, or malformed input so callers can branch on a single nullable result.
  • response.download(path) and response.attachment(path, name?) now keep the Content-Disposition header. The helpers staged it on the response builder but the runtime file response replaced the entire Headers object, so browsers fell back to inline display for any extension the OS happened to know. The merged response also picks up any cookies queued via response.cookie(...) before the download return.
@tekir/dbv0.1.3
DatabaseMay 17, 2026
  • select(...) now rejects column strings that contain parentheses, whitespace, semicolons, or SQL comment markers. The previous behaviour silently passed any string containing ( through as raw SQL so callers could write select('COUNT(*)'), but it also meant a request value that reached select() could splice arbitrary SQL into the query. Aggregate expressions move to a new opt-in selectRaw(expression) API; the built-in count()/sum()/avg()/min()/max() already use the raw path and keep working unchanged.
  • forPage(page, perPage) rejects values below 1 and non-finite numbers instead of producing negative LIMIT/OFFSET. The check funnels through the existing limit() and offset() guards so the rest of the builder sees a single validated state.
  • drizzle-orm bumped to ^0.45.2 to pick up the upstream SQL identifier escaping fix.
SecurityMay 17, 2026
  • JwtGuard.generate(user, { claims }) now throws when claims carries any of the registered names sub, iat, or exp instead of silently letting the caller override the subject or token timestamps. Reserved-claim handling is explicit, so a typo in a custom claim cannot mint a token whose sub does not match the user passed in. Apps that legitimately want a custom sub should switch to a different identity model rather than rewriting the claim.
SecurityMay 17, 2026
  • Social now refuses to construct under NODE_ENV=production when APP_KEY is missing, instead of warning and falling back to unsigned state tokens. Dev and test environments still see the warning and the unsigned fallback so quick spike work keeps moving.
  • Absolute redirect URLs now require allowedRedirects in the config. The previous behaviour treated an empty list as no restriction at all, so a freshly configured app could accept ?redirect=https://evil.com/path end to end. Protocol-relative URLs like //evil.com/path are now resolved against https: and run through the same allowlist instead of slipping past a startsWith('/') shortcut.
  • OAuth state HMAC comparison now runs in constant time. The library already signed the state token; this closes the timing-side-channel ambient to any HMAC verification.
Storage & ParsingMay 17, 2026
  • LocalDriver.getSignedUrl(...) now produces a real HMAC-SHA256 signature keyed by APP_KEY (or a constructor-passed secret) instead of returning the storage key and expiry as a base64 payload. The previous token could be decoded, edited, and re-encoded to forge access to any local file. The new LocalDriver.verifySignedUrl(key, token, expires) checks the signature in constant time and rejects expired URLs; call it from any custom handler that serves files behind signed URLs.
  • LocalDriver constructors that do not configure a signing secret now throw on the first getSignedUrl() call instead of returning a forgeable token silently. Set APP_KEY in the environment or pass secret per disk in config/drive.ts.
  • Windows cross-drive traversal is now blocked. A key resolving to a different drive letter than the disk's root (for example D:\\secret.txt against a C:\\app\\storage root) is rejected before any filesystem access. POSIX behaviour is unchanged.
Storage & ParsingMay 17, 2026
  • Malformed percent-encoded paths (/foo%, truncated sequences) now respond with 400 Bad Request instead of crashing the request with a generic 500. The decode step is wrapped and the failure reason is surfaced to the caller.
  • Dotfile policy now applies to every path segment, not just the trailing filename. With the default dotFiles: 'ignore' setting, GET /.git/config and GET /.env/foo no longer reach the filesystem; dotFiles: 'deny' returns 403 Forbidden for the same paths, and dotFiles: 'allow' opts into the previous behaviour for use cases like .well-known/.
  • The segment scan recognises both / and \\ as separators. Windows previously accepted backslash-encoded requests like GET /assets%5C.git/config because the runtime resolves \\ as a path separator while the dotfile filter only split on /. POSIX behaviour is unchanged.
  • Windows cross-drive paths are now blocked. A request whose decoded path resolves to a different drive than the configured dir is treated as traversal even when relative() does not return a ..-prefixed result. POSIX behaviour is unchanged.
  • The middleware and the StaticProvider fallback now share a single resolveSafePath() helper so both surfaces apply the same encoding, dotfile, and traversal rules.
Storage & ParsingMay 17, 2026
  • Multipart uploads now reject oversize requests before parsing. When the incoming Content-Length exceeds the configured limit, parseMultipart() throws a new PayloadTooLargeError (HTTP 413) instead of buffering the whole payload into the runtime's FormData parser first. Apps that catch framework errors get a clean 413 path; apps that don't were previously paying memory cost for the rejection.
  • A user-supplied tmpFileName() callback can no longer write outside tmpDir. The returned name is basename()-stripped and the final path is verified to stay under the configured directory; paths like ../../etc/passwd are rejected and surface as a tmpFileName validation error on the affected upload rather than escaping containment.
  • PayloadTooLargeError is exported from @tekir/bodyparser for callers that want to branch on it or attach a custom error handler.
Dev ToolsMay 17, 2026
  • Swagger UI assets now load from a pinned [email protected] URL with optional Subresource Integrity. The new ui.cssUrl, ui.jsUrl, ui.cssIntegrity, and ui.jsIntegrity config keys let apps self-host the bundle or pin SRI hashes that the browser enforces before executing the CDN payload.
  • The /docs HTML response now ships a strict Content-Security-Policy with no unsafe-inline. The only inline bootstrap is allowlisted via its SHA-256 hash, so any injected <script> is refused by the browser. The response also carries X-Frame-Options: DENY, X-Content-Type-Options: nosniff, and Referrer-Policy: no-referrer.
  • jsonPath is now embedded into the bootstrap via JSON.stringify(...) plus </script escaping, so a custom config.path cannot break out of the inline script context.
  • Basic auth credential comparison now hashes both sides to fixed-length HMAC digests before timingSafeEqual(). Earlier releases short-circuited on length mismatch, which leaked the password length to attackers timing the response. The auth config surface is unchanged.
FrontendMay 17, 2026
  • The proxy hop into the internal Next listener now drops every header that the upstream Next process should determine for itself. Hop-by-hop fields (connection, upgrade, keep-alive, te, transfer-encoding, proxy-authorization, content-length) and authority fields (host, forwarded, x-forwarded-*, x-real-ip, x-original-url, x-original-host) are stripped before the inner fetch; an internal host matching the loopback port is set. Closes the SSRF/middleware-bypass surface that comes from blindly forwarding a public request's headers to a private upstream.
  • Starter templates and example apps are pinned to next ^16.2.6 to pick up the upstream proxy/middleware advisories.
@tekir/dbv0.1.2
DatabaseMay 12, 2026
  • Internal release covered by 0.1.3 notes.
CoreMay 10, 2026
  • tekir build no longer evaluates the user entry's full top-level. The build path now parses the entry with oxc-parser, walks back from the await tekir({...}) call, keeps only the imports and declarations its argument expression depends on, and writes that to a temporary file the cli imports in place of the original. The tekir() call still fires (so onBuild hooks register and Bun.build runs against the original entry), but app.router.registerDir(...), app.start(...), eager service constructors with TCP connects, scheduler ticks, and fs watchers are skipped — none of which the bundler ever needed. Builds are faster and stop hanging on a misconfigured remote dependency that would never have been needed at build time. New generateBuildEntry(entryPath) is exported for tools that want to drive the same extraction. Dynamic config inside tekir({...}) (env-derived ports, conditional frontend types, computed providers) is preserved verbatim because the call expression is kept as-is; only unreachable top-level statements are dropped.
  • process.env.TEKIR_RUNNER is set to 'build' (via ??=, so an outer caller can pin a different value first) whenever the entry detects cliCmd === 'build'. Pairs with the 'test' value tekir test already exports. Most library code is no longer imported during build at all (the entry extractor sees to that), so the flag is a belt-and-suspenders safety net for the rare entry shape that falls back to a full-entry import; libraries that want to short-circuit eager module-init side effects can still gate on TEKIR_RUNNER === 'build' || TEKIR_RUNNER === 'test'. The contract is documented at /advanced/runner-modes.
Dev ToolsMay 10, 2026
  • tekir build drives the new build-entry extractor in @tekir/core 0.1.29. The cli reads the entry through generateBuildEntry, writes the extracted source to a temp file, sets process.argv[1] to the original entry path so the in-app build dispatcher bundles the real file, then imports the temp source. User entries that the extractor cannot statically resolve (no literal tekir() call, multiple calls, parse error) fall through to a plain full-entry import as a last resort, so the worst case is identical to the historical behaviour.
  • tekir build exports TEKIR_RUNNER=build (via ??=, so a CI script that already pins the value is left alone) before handing the entry to Bun, mirroring the 'test' value tekir test sets. Most library code is no longer imported during build at all (the entry extractor sees to that), so the flag is a belt-and-suspenders safety net for the rare entry shape that falls back to a full-entry import; libraries can still gate on TEKIR_RUNNER === 'build' || TEKIR_RUNNER === 'test' to short-circuit eager init. Convention documented at /advanced/runner-modes.
  • oxc-parser is now a regular @tekir/cli dependency instead of an optional peer. bun add @tekir/cli is enough to get the build-entry extractor and the autoload inliner working out of the box; the long-standing [build] \oxc-parser\ is not installed warning that confused users who never knew what to do with it is gone. @tekir/core keeps oxc-parser as an optional peer for the rare consumer that uses the framework without the cli (e.g. driving Bun.build programmatically with hand-rolled scripts).
CoreMay 8, 2026
  • app.start() honours the tekir test runner signal. When the cli's test subcommand exports TEKIR_RUNNER=test before launching the runtime's native test command, a user entry's top-level app.start(callback) short-circuits instead of binding the env-configured port. The canonical entry shape becomes the unconditional app.start(cb); the per-app if (env !== 'test') guard goes away. Integration tests that genuinely want a real socket pass app.start({ force: true }) to opt back in. The lower-level server.start() (used by @tekir/testing's createTestApp) is unaffected, so request-fixture tests keep working without changes.
  • StartOptions.force?: boolean exported alongside the existing mode and callback fields, for code paths that want a real socket regardless of how the process was launched (dashboards, smoke checks, frontend-env-exposure-style integration tests).
Dev ToolsMay 8, 2026
  • New tekir test [args] command: a thin runner shim that exports NODE_ENV=test plus the TEKIR_RUNNER=test signal app.start() listens for, then hands control to the runtime's native test runner (bun test on Bun, vitest run on Node). The signal is what lets a user entry's top-level app.start(callback) short-circuit when imported by a test file, so the canonical entry shape becomes the unconditional app.start(cb) and the per-app if (env !== 'test') guard goes away. Forwarded args go to the runner verbatim: tekir test --watch, tekir test path/to/file.test.ts, etc. Pairs with @tekir/core 0.1.28's app.start({ force: true }) for integration tests that need a real socket.
FrontendMay 8, 2026
  • Vite middleware honours the tekir test runner signal. When process.env.TEKIR_RUNNER === 'test' the dev gateway block (which would otherwise spin up its own listener on app.port) is skipped, so a user entry can keep frontend: { type: 'vite' } unconditional without a process.env.NODE_ENV === 'test' ? undefined : ... ternary. The build hook (server.onBuild) and the prod static fallback are still registered — they don't bind anything, so they're safe under tests and a tekir build run still produces dist/client/.
FrontendMay 8, 2026
  • Next middleware honours the tekir test runner signal. When process.env.TEKIR_RUNNER === 'test' the internal Next listener is not started (it would have spun up its own random-port HTTP server otherwise). The fallback handler stays registered and no-ops when nextPort is unset, matching the prod-without-dev-server path. Lets a user entry keep frontend: { type: 'next' } unconditional under tests instead of the per-app NODE_ENV ternary.
CoreMay 8, 2026
  • Cleaned up error code naming on every built-in HttpException subclass: the E_ prefix is removed in favour of plain UPPER_SNAKE_CASE matching the HTTP status name (NOT_FOUND, BAD_REQUEST, UNAUTHORIZED, ...). Aligns with gRPC, Google Cloud, AWS Cognito, and Stripe's snake_case conventions; error.code already implies "this is an error" so the prefix is redundant. **Breaking**: callers that branch on err.code === 'E_NOT_FOUND' (etc.) need to drop the prefix. The companion releases ship the same change in @tekir/auth (UNAUTHORIZED), @tekir/authorize (AUTHORIZATION_FAILURE), @tekir/db (ROW_NOT_FOUND), and @tekir/validator (VALIDATION_ERROR).
  • SSE.retry typed as number | string. The runtime path always coerces with String(data.retry) and strips newlines, so passing either shape is safe; the type now matches the implementation. Lets retry flow through configs that hold the value as a string without an intermediate cast.
DatabaseMay 8, 2026
  • Cache.get<T> and the per-store get<T> methods now default T to any instead of unknown. Callers no longer need a redundant generic argument or runtime type guard to read a value back out: await cache.get('user') returns a value you can use directly. Pass an explicit type (cache.get<User>('user')) when you want narrowing back.
DatabaseMay 8, 2026
  • Session.get<T> defaults T to any instead of unknown. Reading session data no longer needs a redundant cast or type guard for the common case (session.get('user') is usable directly). Pass an explicit type when narrowing matters: session.get<UserId>('userId').
SecurityMay 8, 2026
  • Internal release covered by 0.1.6 notes.
SecurityMay 8, 2026
  • Encryption.decrypt<T> defaults T to any instead of unknown. Round-tripped values are usable directly without a generic argument; pass an explicit type when narrowing back to a specific shape (enc.decrypt<User>(token)).
CommunicationMay 8, 2026
  • assertSent and the related sent-history matchers now accept any concrete BaseNotification subclass without a cast. Notification classes commonly take constructor arguments (new WelcomeNotification(userName)); the previous signature required (...args: unknown[]) which is contravariantly incompatible with that pattern, so a WelcomeNotification constructor reference would not type-check.
Dev ToolsMay 8, 2026
  • defineFactory(defaults, model) now accepts a model that implements only create or only createMany. Both methods are optional on the FactoryModel<T> interface; calling factory.create() against a model without create (or factory.createMany() without createMany) throws a clear runtime error pointing at the missing method. Lets you back a factory with a thin wrapper around either an ORM's bulk insert or a single-row insert without faking the other.
Dev ToolsMay 8, 2026
  • @ApiParam accepts an enum field on its options, matching the OpenAPI parameter spec: @ApiParam('status', { type: 'string', enum: ['active', 'inactive'] }) now type-checks and propagates into the generated spec.
  • buildOpenApiSpec(router, config) accepts null/undefined for the router argument. The runtime path already returned an empty paths object for falsy routers; the signature now matches the documented behaviour.
  • RouterLike.get is optional. Spec-only consumers (a plain trie wrapper, a test fixture, a CI script generating JSON) can satisfy the interface without supplying a handler-registration method; swagger() still requires it for live UI wiring.
CoreMay 6, 2026
  • **Breaking**: tekir start is removed. Use tekir serve for every long-running server invocation (dev, local prod, deploy targets like PM2/Docker/systemd). The two diverged historically only because start carried a buggy non-awaited auto-dispatch that double-bound the port when a user entry also called app.start(callback); collapsing to a single command means user code is identical across every launch shape. Migration is a one-line package.json edit: "start": "tekir serve --entry ./dist/index.js ..." (the npm script name keeps working, only the CLI subcommand changes).
Dev ToolsMay 6, 2026
  • All five templates now scaffold bun run start as tekir serve instead of tekir start. The two are aliases on the CLI side, but serve is the canonical name documented in the help output and in @tekir/core 0.1.25's unified dispatch. New projects start on the canonical command, so the deprecated alias only sticks around for existing scripts.
CoreMay 6, 2026
  • Command dispatch is flag-aware. Operators can put options before the command word and the right path still fires: ./server --port 8080 build, bun run index.ts --watch serve, and tekir --entry foo migrate all resolve to their command instead of treating the leading flag as a positional. Critical for compiled binaries, where flag-first invocations are the common shape (PM2/Docker/systemd-style env and port flags). The same scan also feeds the early NODE_ENV setter and the environment detector, so all three layers agree on what the user actually asked for.
  • tekir serve (without --dev) defaults NODE_ENV to production when the shell did not set it, matching the tekir build precedent. The cli bin already exports NODE_ENV='development' before re-execing the watch child for --dev, so the dev path is preserved. Combined with the bun build banner shipped in 0.1.24, every prod entry path now sees the right env without an explicit NODE_ENV=production on the command line.
FrontendMay 6, 2026
  • Fix: production builds no longer return the SPA index.html for unmatched backend paths. Requests to a configured proxyPaths prefix (default ['/api']) that the router did not claim now return a 404 application/json instead of the 200 HTML shell, matching the contract clients already expect from the dev gateway. The same gate applies to compiled binaries' embed map. Custom prefixes still work — set proxyPaths: ['/api', '/v2', '/internal'] to extend the list.
CoreMay 6, 2026
  • tekir build now defaults process.env.NODE_ENV to production at bundle-load time. Apps started with a raw bun ./dist/index.js (the shape PM2, Docker, and systemd typically use) no longer need an explicit NODE_ENV=production on every command line. Runtime-set values still win, so dev-style overrides keep working. The default lands via a bracket-access banner so the bundler's compile-time fold of process.env.NODE_ENV reads is unaffected.
FrontendMay 6, 2026
  • Fix: vite middleware no longer crashes apps with Logger not initialized. Call tekir() first. at startup. The internal vite logger now resolves the framework logger lazily, on each log call instead of once when the middleware is constructed, and falls back to console when the framework logger is not yet populated. This was visible in production bundles where the bundler's module init order put the vite middleware ahead of tekir()'s container setup, or when a duplicate copy of @tekir/core ended up in the bundle and the vite middleware saw a different module-scope _logger than the one tekir populated.
CoreMay 5, 2026
  • Fix: production builds with router.registerDir, cron.registerDir, or emitter.registerDir now reliably register every file in the target directory. The previous release silently registered nothing in some bundles, which surfaced as /api/* routes falling through to the SPA fallback in production while working fine in dev.
  • The fix is generic across decorators. Controllers, jobs, and listeners tagged with any framework-provided or user-defined decorator (@Controller, @Schedule, @OnEvent, custom @Cron, @Subscribe, anything that stamps the registry metadata convention) are picked up the same way. No allowlist of decorator names; the build follows what the runtime would have picked.
FrontendMay 5, 2026
  • Vite is now the dev gateway. It owns the user-configured app.port; the Tekir backend moves to a free port picked automatically and Vite proxies /api (default, configurable via proxyPaths) to it. HMR works natively because the browser connects to Vite directly. The previous architecture proxied through Tekir's HTTP fallback, which forced a hardcoded hmr.clientPort: 5173 that collided with every other Vite project on the box (most visibly: navigating to a Tekir admin panel could serve a sibling project's HTML when 5173 was already taken).
  • All process.cwd() references replaced with the appRoot Tekir injects via the new setup ctx. vite.config.ts discovery, envDir, public/, and dist/client/ paths now resolve from the project root regardless of launching cwd. Same fix removes the process.chdir(import.meta.dir) workaround monorepo apps used.
  • New tekirDefaultsPlugin injects root and build.outDir only where the user's vite.config.ts has not set them. No path alias is defaulted on purpose, since @, ~, and $lib conventions vary per framework and ~ has special semantics in some CSS toolchains.
  • Setup signature is vite(server, config, ctx) (third arg optional). ctx.configStore is what lets the gateway rewrite app.port before server.start() reads it; ctx.appRoot is the project root from tekir(). Older (server, config) integrations still work.
  • Fix: production builds no longer crash with a 500 (EISDIR) when the browser navigates to /. The prod fallback now skips entries that resolve to a directory rather than a file, so GET / correctly falls through to the SPA index.html.
  • Adds get-port@^7 as a runtime dependency.
Dev ToolsMay 5, 2026
  • All five templates (minimal, api, fullstack, with-vite, with-next) now scaffold with tekir CLI scripts: bun run dev becomes tekir serve --dev (watch mode + NODE_ENV=development), bun run build becomes tekir build --outdir ./dist, bun run start becomes tekir start (NODE_ENV=production). @tekir/cli is added to each template's dependencies so the bin shim resolves under node_modules/.bin/tekir after bun install. Replaces the previous bun run --watch index.ts / bun run index.ts build pattern.
  • tekir build runs the entry through the in-app dispatcher, so @tekir/vite 0.1.2's onBuild hook fires and the frontend lands in dist/client/ alongside the backend bundle in dist/index.js. Previously tekir build (in the bin's old code path) called Bun.build directly without ever invoking the entry, so frontend templates that rely on the build hook would silently ship without the client output.
CoreMay 5, 2026
  • tekir() auto-detects appRoot by walking the call stack to find the file that called it and using that file's dirname. Same Error.captureStackTrace mechanism router.registerDir already uses for caller-relative resolution. Falls back to process.cwd() only when no user frame is recoverable. Eliminates the process.chdir(import.meta.dir) workaround monorepo apps need when launched from a parent dir (turbo from repo root, pm2 from /, etc.). Frontend module resolution (createRequire) and config discovery now use the resolved appRoot instead of process.cwd() so the user's local @tekir/vite is found regardless of launch dir.
  • Frontend setup signature extended: setup(server, frontendConfig, { configStore, appRoot }). The third arg is optional, so older (server, config) integrations stay drop-in compatible. The added context lets frontend middleware read and rewrite config (notably app.port) before server.start() reads the value, which is what @tekir/vite 0.1.2 uses to flip the dev architecture and own the user port as a gateway.
Dev ToolsMay 5, 2026
  • tekir build now routes through the entry like every other command, so the user's tekir({...}) instance gets to register onBuild hooks before the bundle runs. Inside tekir() core, argv[2] === 'build' is detected and triggers server.build() (which fires the hooks, e.g. @tekir/vite builds the frontend into dist/client/) followed by Bun.build for the backend bundle. Calling runBuild directly from the bin (the previous behavior) skipped the entry entirely and silently dropped any frontend build, so apps with frontend: { type: 'vite' } shipped a backend bundle without dist/client/.
  • runEntry no longer calls process.exit(0) after the import resolves. Forcing the exit was racing with three legitimate flows: the canonical fire-and-forget server.start().catch(...) pattern (Bun.serve was getting killed mid-bind the moment import completed), in-app dispatchers that exit on their own, and any top-level async work the user awaited. The runtime exits naturally when the event loop drains in all three cases.
CoreMay 4, 2026
  • AST inliner now also folds literal-path runtime fs reads into the bundle, so apps that read small config / template files at startup ship as a single self-contained artifact and run from any working directory. Recognized shapes: readFileSync('./x.json', 'utf-8') becomes a string literal; readFileSync('./x.bin') becomes Buffer.from('<base64>', 'base64'); readFile from fs/promises and Bun.file('./x').text() / .arrayBuffer() chains become Promise.resolve(<literal>). Detection covers named, aliased ({ readFileSync as rfs }), namespace (* as fs), and default imports from fs, node:fs, fs/promises, and node:fs/promises. Eliminates the postbuild scripts, manual file copying, and process.cwd()-relative path probing that monorepo bundles otherwise need at boot.
  • Files larger than 1 MB, dynamic paths (template literals, variables), dynamic encodings, and callback-style fs.readFile are silently skipped, leaving those calls as runtime fs lookups so the inliner never changes call semantics. The inliner is also a no-op when none of the recognized helpers appear in the source, so existing files pay zero analysis cost.
Storage & ParsingMay 4, 2026
  • **Security**: UploadedFile.validateContent() now treats unrecognized magic bytes as outside the extnames whitelist instead of silently allowing them. Previously a renamed malware.exe → malware.jpg slipped through because detectExtname() returned null and the mismatch check was guarded behind if (detected && ...), leaving hasErrors false so the file reached storage. Strict whitelist semantics now: empty buffer → rule: 'content', message: 'Empty file'; magic bytes don't match any known signature → rule: 'content', message: 'Unrecognized file content'; detected format is not in the whitelist → rule: 'content', message: 'File content (.X) is not in allowed types: ...'; declared extension does not match the detected format → rule: 'extname'. The non-strict path (no extnames option) is unchanged.
Storage & ParsingMay 4, 2026
  • **Breaking**: ctx.files is now a method, not a MultipartFiles collection. Multi-file fields are read with ctx.files(name) (returns UploadedFile[]) instead of ctx.files.files(name). Matches AdonisJS' single-method-per-shape pattern (ctx.file() / ctx.files() / ctx.allFiles()) and removes the awkward files.files double-dot. The MultipartFiles class is still exported for advanced use; the parser still produces it internally.
  • All three accessors are installed on ctx for every request, including non-multipart ones, with a no-op fallback (ctx.file() → undefined, ctx.files() → [], ctx.allFiles() → []). Removes the optional-chain dance from controllers, so const avatar = ctx.file('avatar') works in any handler regardless of content-type.
  • ctx.file(name) now returns UploadedFile | undefined (was ... | null) so the entire surface lines up on undefined for the absent case.
CoreMay 4, 2026
  • AST inliner now emits a per-call-site IIFE picker instead of a shared __tekir_pick helper. 0.1.18 wrapped the helper body in new Function("m", "<body>"), but Bun's bundler optimizer still parses the literal body and folds the call sites back to m.default ?? m because every call site's argument is a static namespace import whose shape is known at bundle time. A separate IIFE per call site, plus a reflection probe through Object.prototype.hasOwnProperty.call(_m, "default"), blocks the static-shape analysis: the bundler cannot prove _m is an own-property holder for default purely from the namespace synthesis, so the body survives intact in the output. The fix has been verified on real production bundles (148 hasOwnProperty.call references survive in a typical sevk-shaped app, controllers and cron jobs all register).
CoreMay 4, 2026
  • Hardens the AST inliner's __tekir_pick helper against Bun's bundle-time optimizer. 0.1.17 emitted the picker as a regular function declaration; Bun's optimizer was inlining the body into each call site and constant-folding the result down to m.default ?? m, which on a named-export controller (export class FooController, no default) collapsed back to the bare module namespace and crashed register(...) with Object is not a constructor. The picker is now built from a string literal via new Function("m", "<body>") so the bundler only sees the literal at build time and cannot fold the body. Function compiles once at app boot, no per-request impact.
CoreMay 4, 2026
  • AST inliner now picks the right export for export class FooController (named export, no default) bundles. The previous output emitted (__tekir_inline_X.default ?? __tekir_inline_X) for every imported file, which collapsed to the namespace object when no default existed and made register(...arr) call new <namespace>, producing Object is not a constructor at boot in production builds. Each rewritten registerDir/loadDir call now goes through an injected __tekir_pick(mod) helper that mirrors the runtime defaultPick (default first, then single named export, then decorator-tagged class via __prefix/__routes/__schedules/__listeners, then first function-typed named export, then the namespace as a last resort). Build and runtime now resolve the same export shape for the same file.
  • Helper is inlined into the transformed source (one definition per file that has loadDir/registerDir call sites), so the picker logic rides along inside the bundle without adding a new runtime dependency on @tekir/core for files that did not already import it.
CoreMay 4, 2026
  • Fixes 0.1.15's caller capture in router.registerDir. The previous version dynamically imported loadDir and captureCallerFile inside the registerDir method, which placed the call across an await boundary, so by the time the stack was inspected the user's frame was gone and Bun's only remaining frames (native:1:11) leaked through to be used as the resolution base. The bin would then warn (resolved against native) and load nothing. The fix moves both helpers to top-level static imports so the caller is captured synchronously on entry, before any await runs.
  • captureCallerFile's stack-frame parser tightened: a frame's path must look like an absolute filesystem path (Unix /... or Windows <drive>:\...) or a file:// URL, otherwise it is rejected. That blocks Bun's native (after the :1:11 suffix is stripped), Node's node:internal/..., anonymous <anonymous> frames, and any other synthetic engine markers from being treated as user code.
CommunicationMay 4, 2026
  • Fixes 0.1.3's caller capture in emitter.registerDir. Same root cause as @tekir/core 0.1.16: await import('@tekir/core') ran before captureCallerFile, so the user's frame was already gone by the time the stack was inspected and the warning printed (resolved against native). Static top-level imports for captureCallerFile/loadDirEntries keep the capture synchronous on registerDir entry. Pair with @tekir/core 0.1.16+.
CommunicationMay 4, 2026
  • Fixes 0.1.3's caller capture in cron.registerDir. Same root cause as @tekir/core 0.1.16: await import('@tekir/core') ran before captureCallerFile, so the user's frame was already gone by the time the stack was inspected and the warning printed (resolved against native). Static top-level imports for captureCallerFile/loadDirEntries keep the capture synchronous on registerDir entry. Pair with @tekir/core 0.1.16+.
CoreMay 4, 2026
  • **Breaking**: router.registerDir(...) resolves a relative path against the caller's own directory, not process.cwd(). Aligns runtime resolution with what the AST inliner already does at build time, so the standard monorepo dev pattern (cd <root> && tekir serve --dev --entry api/index.ts) works without rewriting paths: await router.registerDir('./controllers') from api/index.ts resolves to api/controllers regardless of the cwd. Base directory captured via Error.captureStackTrace. Pass options.from = process.cwd() to keep the old cwd-relative behavior for a specific call site.
  • New LoadDirOptions.from accepts a file:// URL or absolute path (typically import.meta.url) to set the resolution base explicitly when the auto-captured caller is not the right answer.
  • captureCallerFile(boundary) exported from @tekir/core so other registries (cron, emitter, custom) can apply the same caller-relative resolution. Both Bun and Node honor Error.captureStackTrace(obj, fn); the helper handles the format differences (Bun raw paths vs. Node file:// URLs) internally.
CommunicationMay 4, 2026
  • **Breaking**: emitter.registerDir(...) now resolves a relative path against the caller's own directory, not process.cwd(). Matches the AST inliner's build-time behavior so await emitter.registerDir('./listeners') from api/index.ts lands at api/listeners regardless of cwd, and the cd <root> && tekir serve --dev --entry api/index.ts monorepo dev pattern works as-is. Pass options.from = process.cwd() for the old cwd-relative behavior on a specific call site.
CommunicationMay 4, 2026
  • **Breaking**: cron.registerDir(...) now resolves a relative path against the caller's own directory, not process.cwd(). Matches the AST inliner's build-time behavior so await cron.registerDir('./jobs') from api/index.ts lands at api/jobs regardless of cwd, and the cd <root> && tekir serve --dev --entry api/index.ts monorepo dev pattern works as-is. Pass options.from = process.cwd() for the old cwd-relative behavior on a specific call site.
Dev ToolsMay 4, 2026
  • Bin shebang now routes through Bun (#!/usr/bin/env bun). Bun is position-strict on its own --env-file flag, so --env-file=... tokens after the script path pass through to the bin's argv where the in-bin loader filters missing files with a warning and keeps the rest. The tekir shim created by bun add -g @tekir/cli (or npm i -g) regenerates with this hint on upgrade, so existing scripts that chain --env-file flags work unchanged.
  • --envfile (no hyphen) added as a Node-host-safe alias of --env-file. The hyphenated form is intercepted by Node's runtime before the bin runs and hard-errors on a missing file; the un-hyphenated form is unrecognized by Node's CLI parser and is forwarded to the script's argv unchanged. Useful when invoking the bin directly under Node (node node_modules/@tekir/cli/bin/tekir.mjs --envfile=path serve).
  • Env files declared in package.json under "tekir": { "envFiles": ["...", "..."] } are loaded automatically before the entry runs. Recommended for keeping per-package .env chains out of every script. Paths in JSON load first, then any CLI --env-file / --envfile flags layer on top with later-wins precedence; shell-provided env always wins both.
CoreMay 4, 2026
  • runBuild, parseBuildArgs, and BuildArgsError are now public exports of @tekir/core, so the new @tekir/cli package and any user driving Bun.build programmatically share one implementation. Same flag surface as the in-process bun run index.ts build dispatcher, plus optional extraPlugins / extraExternals / logger overrides on the JS API for advanced setups.
  • Build flag parser rewritten on top of node:util.parseArgs (Node stdlib, also available in Bun) so unknown flags, missing values, and bad --define / --sourcemap / --format / --env values surface as clear errors instead of being silently dropped.
  • Forwarded flags expanded to match bun build more completely: --format esm|cjs|iife, --banner, --footer, --drop (multi), --env inline|disable|<PREFIX>*, --public-path, --no-bundle, --keep-names, granular --minify-syntax / --minify-whitespace / --minify-identifiers, --entry-naming, --chunk-naming, plus --metafile <path> and --metafile-md <path> for bundle analysis output. Granular minify flags emit Bun's object form so users can pick a subset (e.g. --minify-syntax alone).
  • Refused with a clear error when invoked from inside a compiled binary. Detection is hybrid: Bun.main virtual-fs marker (~BUN) plus process.execPath basename check, two independent signals so a single Bun version drift does not break detection.
CoreMay 4, 2026
  • Pairs with the new @tekir/cli package. The CLI's tekir build command imports createInlinerPlugin from @tekir/core and runs Bun.build directly without touching the entry file, so apps with side-effect-heavy module loads (Redis subscribers, message-bus clients, fs watchers) stay quiet during build. Drop-in replacement for bun build api/index.ts --outdir ./dist [...] once you install @tekir/cli.
  • Default loadDir/registerDir picker handles export class FooController (named export, no default) automatically. The picker tries mod.default first, falls through to the single named export, prefers a decorator-tagged class (__prefix/__routes/__schedules/__listeners) when there are multiple named exports, then the first function-typed export, and finally returns the namespace itself. Apps no longer need a custom pick: m => m.default ?? Object.values(m).find(...) for every registry call.
  • registerDir warnings now name the source file: [router.registerDir] core/controllers/typo.ts: skipped (unrecognized export shape: object). Same wording on cron.registerDir and emitter.registerDir. loadDirEntries(path, options) is exported alongside loadDir for callers that need the file path next to every picked export.
  • registerDir (router, cron, emitter) prints a single warning when it loaded zero modules, with a hint pointing at the inliner plugin. Replaces the previous silent failure where a misconfigured production bundle would just have no controllers/jobs/listeners with no log line explaining why.
  • createInlinerPlugin is exported from @tekir/core so plain bun build --outdir ./dist bundles (without --compile) can pick up the same inlining: Bun.build({ plugins: [await createInlinerPlugin()] }). Without it, runtime registerDir calls in those bundles can't see the source files and silently load nothing.
  • bun run index.ts build --outdir ./dist now runs a plain Bun bundle through the tekir CLI (no --compile required). The inliner plugin is auto-injected, so loadDir/registerDir calls are still followed by the bundler, and the existing --target / --minify / --sourcemap / --external / --define / --plugin / --splitting flags are all forwarded. CLI-only build setups can keep await router.registerDir('./controllers') instead of writing a Bun.build({...}) script.
CommunicationMay 4, 2026
  • registerDir warning now names the source file: [emitter.registerDir] core/listeners/typo.ts: skipped (unrecognized export shape: object). Replaces a generic Skipping ... log line that did not say which file dropped out.
  • Single No modules loaded warning with an inliner hint when registerDir matches zero modules. Replaces the previous silent failure where a misconfigured production bundle would just have no listeners attached with no log line explaining why.
CommunicationMay 4, 2026
  • registerDir warning now names the source file: [cron.registerDir] core/jobs/typo.ts: skipped (unrecognized export shape: object). Replaces a generic Skipping ... log line that did not say which file dropped out.
  • Single No modules loaded warning with an inliner hint when registerDir matches zero modules. Replaces the previous silent failure where a misconfigured production bundle would just have no jobs attached with no log line explaining why.
CoreMay 4, 2026
  • loadDir(path) returns the default export of every file in a directory, so registries like controllers, cron jobs, listeners, and commands no longer need a 25-line import block. Pass a custom pick callback to grab a named export, match / ignore regexes to filter, and recursive: true to walk subdirectories. Works on Bun and Node by routing through @tekir/runtime's readDirRecursive, which uses Bun.Glob on Bun for the directory scan and falls through to node:fs/promises on Node.
  • router.registerDir(path) wires up a whole controllers folder in one line. It auto-detects three export shapes per file: decorator classes (the @Controller + @Get/@Post/... pattern, registered via router.register), functional registrars (export default (router) => { ... }, invoked with the router), and classes with a register(router) method (a fresh instance is constructed and its register is called). Files whose default export does not match any pattern are skipped with a console.warn so misconfigured exports surface during boot.
  • bun build --compile now bundles the files referenced by loadDir('path') and *.registerDir('path') calls. The compile pipeline auto-injects an AST-based inliner (powered by oxc-parser) that finds literal-string folder calls, lists the directory at build time, and replaces each call with explicit static imports so Bun's bundler can follow them. Comments, string literals, computed-arg calls, and unrelated identifiers are left alone. oxc-parser is an optional peer dependency; install it with bun add -d oxc-parser to opt in. When the parser is missing, compile prints a one-line install hint so the silent failure mode does not bite.
CoreMay 3, 2026
  • readDir(path) and readDirRecursive(path, options) list directory contents across runtimes. The recursive walker uses Bun.Glob on Bun and falls back to a depth-first node:fs/promises walk on Node. Skips node_modules, .git, and dotfiles by default; pass extensions to filter by suffix and ignore to widen the skip list.
CoreMay 3, 2026
  • ctx.$responseHeaders is the new way for middleware to attach response headers. Anything written here lands on the outgoing response right before it goes on the wire, on success, error, and framework-handled-error paths alike. CORS, request id, server timing, and any other header-attaching middleware now work from any position in the chain instead of breaking silently when an error handler sat between them and the route.
  • Unmatched paths now run the global middleware chain before responding 404, so cors(), request loggers, and other hooks observe the request and stamp their headers on the response. Previously a stray request to /non-existent skipped the chain entirely and the browser saw a generic CORS error on what was actually a 404.
  • ctx.request exposes path, host, hostname, protocol, origin, and completeUrl as direct properties. Routes that touch any of these get a single upfront URL parse; routes that only read request.url / request.method skip the parse entirely.
  • Vary is appended (not overwritten) when both a handler and middleware set it, so a Vary: Accept-Encoding from the cache layer keeps living next to the Origin token CORS adds.
CommunicationMay 3, 2026
  • emitter.registerDir(path) loads every file in a folder and binds whatever each module exports as a listener: decorator classes (the @OnEvent pattern with __listeners metadata) go through emitter.register, functional registrars (export default (emitter) => emitter.on(...)) are invoked with the emitter, and classes with a register(emitter) method are constructed and called.
CommunicationMay 3, 2026
  • cron.registerDir(path) loads every file in a folder and registers whatever each module exports as a job: decorator classes (the @Schedule('* * * * *') pattern with __schedules metadata) go through cron.register, functional registrars (export default async (cron) => cron.add(...)) are invoked with the manager, and classes with a register(cron) method are constructed and called.
CoreMay 3, 2026
  • Server idle timeout default is now 120 seconds, comfortably above typical SSE keepalive intervals (15-30 s) and long-poll cycles, while still reaping stuck or slowloris-style connections. Apps that need genuinely long-lived idle connections can pass idleTimeout: 0 to disable the timeout entirely; any other finite value is honored.
SecurityMay 3, 2026
  • When the chain throws and no inner middleware set ctx.$result along the way, the middleware no longer coerces the missing result to a 204. The previous behavior won the race against an outer error handler that returns the real error response (because the framework only adopts a returned response when ctx.$result is still undefined), so the client could see a CORS-OK 204 instead of a 401/500. Now the throw simply propagates and the outer handler builds the actual response. When an inner middleware did set ctx.$result before re-throwing, CORS headers still merge onto it as before.
CoreMay 3, 2026
  • Long-lived streams (Server-Sent Events, long-polling, slow file downloads) no longer get cut off mid-flight. The server's idle timeout default is configurable; set app.idleTimeout in your config (or pass idleTimeout to server.configure({...})) to override the framework default.
SecurityMay 3, 2026
  • Error responses now carry CORS headers regardless of middleware order. The middleware wraps await next() in a try/catch, runs the header merge whether the chain resolved or threw, and re-throws so outer error handlers and loggers still see the original error. Without this, putting cors() ahead of an error-handling middleware silently dropped Access-Control-Allow-Origin from every error response, and the browser blocked the response with a generic CORS error even though the API responded correctly.
CoreMay 3, 2026
  • **Breaking**: tekir() no longer scans <root>/env.ts, <root>/src/env.ts, <root>/config/, <root>/start/, or <root>/commands/ automatically. Pass envFile, configDir, and startDir explicitly to keep a file-based layout: await tekir({ envFile: 'env.ts', configDir: 'config', startDir: 'start' }). With nothing set, tekir loads no files; everything is inline. This stops the framework from running unrelated root scripts named env.ts (e.g. interactive .env setup CLIs in monorepos) when an app boots.
  • OPTIONS preflight on a path that registered only specific methods (e.g. POST /login) now reaches the global middleware chain. Before, Bun.serve returned 405 and cors() never saw the preflight. The router now synthesizes a 204 OPTIONS handler at every path that did not register one explicitly, so middleware can intercept and short-circuit with the proper preflight response.
SecurityMay 3, 2026
  • Actual responses (not just preflight) now carry CORS headers. The middleware previously stashed Access-Control-* values on ctx.store.__corsHeaders for downstream code to apply, but nothing in tekir read them back, so browsers blocked every cross-origin POST/GET even when preflight succeeded. The middleware now injects the headers directly onto the response after the handler runs.
  • Routes that return a raw Response object (SSE streams, file downloads, custom payloads) now get CORS headers too. The middleware coerces whatever the handler returned into a Response and merges the headers in, preserving the original status, body stream, and any handler-set headers.
  • Vary: Origin is appended on every CORS-injected response so HTTP caches do not serve a response built for one origin to a request from another. When the handler already set Vary, Origin is added to the existing list instead of overwriting it.
Dev ToolsMay 3, 2026
  • The api and fullstack templates now wire tekir() with explicit envFile, configDir, and startDir paths, matching the loader contract in @tekir/core. New projects scaffold and boot end-to-end without any extra setup.
Dev ToolsMay 3, 2026
  • Adapts to @tekir/core 0.1.8's explicit autoload paths. createTestApp() now auto-detects env.ts, config/, and start/ under the app root and forwards them to tekir(), so existing test suites continue to work with no changes. Pass envFile: false, configDir: false, or startDir: false to opt out of any of them, or pass a custom path string to override.
Dev ToolsMay 3, 2026
  • Tests now run on Node as well as Bun. The package detects the runtime at load time and re-exports bun:test on Bun (built-in, zero install) or vitest on Node (peer dependency: bun add -d vitest). The exported names stay the same (test, describe, expect, beforeAll/afterAll/beforeEach/afterEach, mock, spyOn, jest) and the bun-style helpers are mapped onto vitest's vi.* equivalents so handler code stays identical.
CoreMay 2, 2026
  • request.headers() no longer requires the DOM.Iterable lib in the consumer's tsconfig. Some app tsconfigs only pull in DOM, which made the previous Headers.entries() call fail to type-check at the consumer side. Switched to Headers.forEach, available in plain DOM.
CoreMay 2, 2026
  • Header conversion no longer requires the DOM.Iterable lib in the consumer's tsconfig. Replaced the Headers.entries() call in the Node.js server adapter with Headers.forEach, which is part of plain DOM.
CoreMay 2, 2026
  • Body parser failures no longer crash routes with a generic 500. When the declared Content-Type does not match the actual payload (empty body with application/json, malformed urlencoded, etc.) the parse error is captured on ctx.bodyError so handlers and middleware can respond with a real 400.
  • ctx.response.status(code).json(...) now actually carries the status across the chain. The compiled fast path used to silently fall back to 200; routes that chain a status setter automatically switch to a stateful response object.
  • Middleware return values are picked up automatically. Returning a Response (or anything else) from a middleware sets it as the route result, so return response.unauthorized() works the way Express, Koa, and Hono users expect without remembering ctx.$result =.
Dev ToolsMay 2, 2026
  • client.options(path) for testing CORS preflight handlers and any other OPTIONS route. Mirrors the existing get/post/etc. surface and is also available on the withHeader/withToken/withBasicAuth proxies.
  • Streaming endpoints no longer hang assertions. Pass { stream: true } to skip the body drain on SSE, long-poll, and download routes; status and headers come back immediately, and the raw Response is exposed on res.raw if you want to read the stream yourself.
  • res.assertError({ message, statusCode }) transparently unwraps the framework's { error: { ... } } envelope, so error assertions work the same whether the route returns a wrapped HttpException payload or a plain { message, statusCode } body.
CoreApril 30, 2026
  • tekir() accepts an inline routes callback so single-file apps can register routes without destructuring the router first. The callback runs after providers boot, so service() resolves to live instances inside it, and the methods passed in are pre-bound, so destructuring ({ get, post }) works without losing this.
Dev ToolsApril 29, 2026
  • Renamed apiClient(baseUrl) to client(baseUrl). Update imports: import { client } from '@tekir/testing'.
Dev ToolsApril 29, 2026
  • Optional HTTP Basic auth on the Swagger UI and JSON spec. Pass auth: { username, password, realm? } to gate /docs, /docs/, and /docs/json. Constant-time credential comparison; sends a 401 with WWW-Authenticate: Basic realm="docs" when credentials are missing or wrong.
DatabaseApril 29, 2026
  • RedisCacheStore now accepts any redis-like client without requiring a matching send() signature. The interface dropped send and connected fields so @tekir/redis, ioredis, and node-redis clients all type-check directly without casts.
DatabaseApril 29, 2026
  • Memory and database session stores type-check cleanly without @tekir/redis installed. The redis store loads only when the redis driver is selected.
DatabaseApril 29, 2026
  • RedisCacheStore now accepts both @tekir/redis (returns boolean) and node-redis / ioredis (return number) clients without a TypeScript cast. Existence checks normalise both shapes at the call site.
CoreApril 29, 2026
  • response.redirect.back(fallback?) sends users back to the page they came from. Reads the Referer header and restricts it to same-origin URLs, so attackers cannot bounce users off-site through a crafted referer. Falls back to the provided URL (or /) when the referer is missing or cross-origin.
DatabaseApril 29, 2026
  • Memory and database cache setups now type-check cleanly without @tekir/redis installed. The redis store is loaded only when the redis driver is used.
SecurityApril 29, 2026
  • Apps that augment TekirAuthUser with their own model shape (for example extends ModelFields<User>) no longer trip on a base id type conflict. The augmentation hook is now field-free so any user model fits.
CommunicationApril 29, 2026
  • Memory and database queue setups now type-check cleanly without @tekir/redis installed. The redis backend is loaded only when the redis driver is used.
DatabaseApril 29, 2026
  • Added cache() middleware that caches full HTTP responses by URL with TTL, conditional revalidation via If-None-Match, and Vary header support. Skips mutating methods, error responses, and no-store requests by default.
  • Added setDefaultCacheStore() so CacheProvider auto-wires the middleware. Routes can use cache({ ttl: 60 }) once the provider is registered, no store option needed.
DecoratorsApril 29, 2026
  • Added the @Cache decorator. It is a thin wrapper around @Middleware([cache(opts)]) from @tekir/cache, so controller methods can opt into HTTP response caching with a single line.
CoreApril 27, 2026
  • Added a NOTICE.md and inline attribution comments crediting Elysia (MIT, Copyright 2022 saltyAom) for the implementation details that were adapted from its source: the AOT body parser's charCodeAt(12) content-type switch, the arrow-handler source separator, the query parser's bit-flag layout, the SSE helper, and the beforeHandle / afterHandle lifecycle hooks.
Dev ToolsApril 27, 2026
  • request_logger middleware now reads the response status via ctx.response.getStatusCode() so it type-checks under strict TypeScript.
  • AuthController reads validated bodies through RegisterBody / LoginBody types exported from validations/auth.ts (z.infer<typeof schema>), so destructured fields are typed instead of unknown.
Dev ToolsApril 27, 2026
  • Auth wiring simplified: the kernel no longer needs an explicit silentAuth()/attachAuth() middleware. AuthProvider registers its own ctx.auth initializer. Public routes like /register and /login just call auth.login(user) directly.
  • AuthController now uses the natural destructured signature ({ body, response, auth }) => ... and works after a login swap (the framework mutates ctx.auth in place).
  • Logout endpoint switched to auth.logout() (no-op for JWT, since JWT is stateless); the comment in the controller points the user at auth.revokeAll() for revocable token guards.
  • Test files in templates renamed from auth.test.ts to auth.test.ts.template so the framework's own test runner stops trying to execute them in-place. The installer still strips .template when scaffolding.
SecurityApril 27, 2026
  • AuthProvider now wires up a lightweight ctx.auth initializer onto the router automatically. Apps no longer have to add a global middleware in start/kernel.ts for handlers like /register to call auth.login(...).
  • auth.login() and auth.logout() mutate ctx.auth in place instead of replacing the object, so destructured handlers like ({ auth }) => { await auth.login(user); auth.generate() } keep working after a login swap.
  • New attachAuth() middleware exported for apps that prefer wiring it manually instead of relying on the provider.
Dev ToolsApril 27, 2026
  • The in-memory sqlite override now happens before tekir() boots, so the database provider opens :memory: from the start. Previously the override mutated config after the connection was already open, which left tests pointing at the dev sqlite file.
  • createTestApp() reads the app's config/database.{ts,js,mjs} (when present) and clones it with sqlite paths swapped to :memory:, preserving any other connections and provider-specific options.
DatabaseApril 27, 2026
  • Split CacheConfig (loose, what config/cache.ts exports) from a new CacheManagerOptions (strict, what the Cache class accepts). The provider expands driver configs before instantiating, so direct new Cache({...}) callers and config-driven setups both type-check.
Dev ToolsApril 27, 2026
  • createTestApp() now picks an in-memory sqlite by default and runs pending migrations from database/migrations automatically. Tests no longer need a manual setup file with MigrationRunner boilerplate.
  • appRoot is optional and defaults to process.cwd(). Pass a string (typically import.meta.dir) only when tests live in a nested folder.
  • New options on createTestApp: migrate (force-on/off auto-migration) and inMemoryDb (opt out of the sqlite override).
SecurityApril 27, 2026
  • Guard configs accept a model shortcut (any class with a static find(id)) and skip the resolver boilerplate. The shipped templates now pass model: User directly.
  • findUser config field renamed to resolve to reflect that the resolver works for any auth subject, not just users (members, accounts, admins, …).
  • New AuthModel type exported for typing custom resolver shortcuts.
CoreApril 27, 2026
  • AppConfig interface added so config/app.ts can be authored with satisfies AppConfig for autocomplete on the framework-known fields without losing extensibility.
  • Service providers can now expose CLI commands via a static commands = [...] array. Registered providers contribute their commands automatically, so apps no longer need a start/commands.ts to surface things like migrate or seed.
DatabaseApril 27, 2026
  • CacheConfig.stores now accepts driver-config objects ({ driver: 'memory' }) alongside CacheStore instances, so apps can declare stores in config/cache.ts without importing store classes.
@tekir/dbv0.1.1
DatabaseApril 27, 2026
  • static hooks = { beforeCreate: [(user: User) => ...] } on a subclass now type-checks. Previously the hook callback's typed parameter clashed with the base class's stricter unknown signature.
  • DatabaseProvider now auto-exposes its migration commands (migrate, migrate:rollback, migrate:status, migrate:fresh, etc.). Apps that register the provider get the commands wired into bun run index.ts <command> without listing them in start/commands.ts.
Dev ToolsApril 27, 2026
  • api and fullstack templates now declare every package they actually import (@tekir/bodyparser, @tekir/cron, @tekir/drive, @tekir/emitter, @tekir/notification). Fresh scaffolds no longer surface Cannot find module errors in services.ts.
  • All start/*.ts files cleaned up: broken imports for non-existent listeners, schedules, controllers, and middleware were removed. Templates boot end-to-end on the first bun run dev.
  • AuthController added with POST /api/auth/register, POST /api/auth/login, POST /api/auth/logout, and GET /api/auth/me. Validation schemas live under validations/, mapped via #validations/* import alias and a matching tsconfig path.
  • Database setup moved from db.exec(Model.createSQL) to a real migration pipeline: database/migrations/ with timestamped files, run via bun run index.ts migrate. Demo migrations create the users and auth_tokens tables.
  • Tests added: tests/auth.test.ts covers register/login/logout/me end-to-end. Backed by @tekir/testing's in-memory sqlite + auto-migration so they're hermetic.
  • request_logger middleware ships out of the box and is wired into the kernel.
  • Generated config files use satisfies (AppConfig, AuthConfig, CacheConfig, CorsConfig, DatabaseConfig, HashConfig, LoggerConfig) for autocomplete without losing literal-type narrowing.
  • Auth config now passes model: User instead of a findUser arrow, removing per-app boilerplate.
  • Health controller no longer leaks server time (new Date().toISOString() removed from the response).
  • Every template ships a .gitignore covering node_modules, .env, build artifacts, and sqlite files.
  • zod is declared as a dependency where templates use it, and @tekir/db migration commands are auto-registered through DatabaseProvider.commands so no manual start/commands.ts is needed.
Dev ToolsApril 27, 2026
  • Every template now ships an eslint.config.ts with the required dev dependencies (eslint, @eslint/js, globals, typescript-eslint) and lint / lint:fix scripts wired up.
  • with-vite template upgraded to Vite 7.
Dev ToolsApril 27, 2026
  • Dropped the misleading await before start(...) in every template (start() is sync).
  • with-next template now scaffolds a Next.js 16 project. Cleaned up next.config.ts and tsconfig.json so a fresh scaffold runs without warnings on first boot.
  • Sqlite-backed templates with path: './database/app.sqlite' boot cleanly on a fresh project even without a pre-existing database/ directory.
CoreApril 27, 2026
  • bun run index.ts build --compile now exposes the full Bun compile surface: --define KEY=VAL, --exec-argv, --asset-naming, --splitting --outdir, --plugin, plus autoload toggles for tsconfig, package.json, .env, and bunfig.
  • frontend: { type: 'vite' } apps can now be compiled into a single executable.
  • Compiled builds auto-clean their intermediate dist/<buildDir> after writing the binary. Pass --keep-artifacts to inspect the build output.
CoreApril 27, 2026
  • openDatabase() now creates missing parent directories automatically. A path like './database/app.sqlite' works on a fresh project even when no database/ folder exists yet.
UtilitiesApril 27, 2026
  • Now installs alongside @tekir/runtime 0.1.1 so the SQLite auto-mkdir fix reaches downstream apps.
FrontendApril 27, 2026
  • Single-executable support via bun run index.ts build --compile. Vite output is bundled into the binary and served at runtime with no node_modules on disk.
  • Auto-discovers your vite.config.ts (or .js, .mts, .mjs) in the project root, so plugins like @vitejs/plugin-react, @vitejs/plugin-vue, and the Svelte and Solid plugins are picked up correctly in both dev and production builds.
  • Inlines only VITE_* env vars into the client bundle so secrets stay server-side.
FrontendApril 27, 2026
  • Now supports Next.js 16. Peer range is >=14.0.0 <17.0.0.
v0.1.0Initial ReleaseApril 1, 2026
  • Core framework with router, DI container, and kernel lifecycle
  • ActiveRecord ORM with SQLite, PostgreSQL, and MySQL support
  • Fluent query builder with joins, aggregates, and pagination
  • File-based migration system with Schema Builder
  • Guard-based authentication (JWT, session, database tokens)
  • Policy-based authorization system
  • Request validation with Zod integration
  • Email via SMTP, Sevk, Resend, Mailgun, and SES
  • Background job queues with retry and delay
  • Multi-channel notifications (mail, database, push)
  • Typed event emitter with wildcard support
  • Cron job scheduler with overlap safety
  • Multi-driver caching (memory, Redis, database)
  • Session management with flash messages
  • File storage with local, S3, R2, and GCS drivers
  • Structured logging with Datadog, Loki, and Pino transports
  • CLI framework with args, flags, prompts, and terminal UI
  • Engine-agnostic views (Eta, EJS, Pug, React)
  • CORS, CSRF, rate limiting, and security headers
  • Password hashing (bcrypt, argon2, scrypt) and encryption
  • OpenAPI / Swagger documentation
  • HTTP testing utilities with chainable assertions
  • Internationalization with JSON locale files
  • Health check endpoints
  • Redis client with pub/sub and pipeline support
  • Optional decorator packages (HTTP, DB, cron, events, Swagger)
  • Generic decorator creation toolkit
  • Vite and Next.js frontend integration
  • 45 first-party packages, 5,000+ tests