Sunday, November 12, 2023

3.4.0 - Under the Hood

What's Changed

This release is aimed at bringing quality of life improvements to general tooling, with a strong emphasis on compiler orchestration.

Compiler Overhaul

The compiler orchestration has been completely rewritten.  The logic for managing concurrent build requests, watching files, communicating with running servers, etc. has been centralized into a compiler server.  This server owns the lifecycle for compliation, restarting on workspace invalidations, and the like.  The logic that was previously shared between the compiler and the VS Code extension now lives solely within the compiler.  

This overhaul also precipated an audit of all file-based operations, ensuring atomicity of write behavior and also that concurrent requests would not interfere (e.g. a request to clean occurring in the middle of a build cycle).

File Watching 

Previously, the manifest module provided primitives for file watching, and the base module would build upon that for simple use cases.  The main need for watching within the framework, was to watch for changed source files. These file watches were extremely problematic across multiple operating systems, and ensuring everything worked correctly was a challenge.  Even with the introduction of @parcel/watcher, challenges still arose. 

To ensure the primary use case was honored, the compiler server now provides a stream of change events that fire after the file is guaranteed to be compiled, and the manifest has been updated.  This means that filesystem watching is no longer a concern for the framework, but listening to compiler changes is.  The main benefit here is that the watch logic can be replaced/changed/etc, and it only affects the compiler.

As a note, there is still need a for watching files in the email-compiler module, and so a simple integration with parcel's watcher makes quick work of it.  

Config

To the outside, there is nothing that has changed here.  The biggest difference is now a clearer contract for prioritization of various config sources, as well as the ability to support multiple files. This has a direct benefit for the mono-repo setup. but provides a clear contract of what happens when the same file shows up in multiple folders.  This follows the resource provider pattern of, mono-repo >> module >> resource paths.  

General Simplifications

As with every release, one of the underlying goals is to align the codebase with standards within the NodeJS ecosystem, as well as incoporating changes to the built-in APIs (e.g. the switch to fetch).  This release sees more and more internal code removed in favor of standards or simpler solutions.  The net result is a smaller, simpler code base that follows closer and closer with best practices.  

The beauty of this process is that the public API shape is unchanged, which allows for such an invasive change to register as a minor release.  

Whats Next?

The ecosystem is still waiting on ES Decorators being fully ratified (including parameter decorators), including support for standalone functions.  Once all the necessary features are available within Typescript, the framework will overhaul decorator behavior, and allow for isolated functions where it make sense (e.g. DI factory functions, standlone rest routes, caching, etc).

Sunday, June 25, 2023

3.2.0 - You're an All-Star

What's Changed

The hits keep coming and they don't stop coming.  Hot on the heels of the 3.1.0 release, the 3.2.0 release has landed, with a another round of substantive improvements.

Email Templating Overhaul

The email templating system has been completely overhauled. What was previously the Email Template module has now been broken into two sub modules:
  • Inky Module - This module now hosts the JSX components (no more html files) used for generating the Inky email content.  This module has zero dependencies on the compilation process, and so can safely be included as a non-dev dependency without fear of affecting the final output size. 

    The biggest change here is that templates are now compilable (and checkable) for errors, etc.  This was the last major piece of the framework that wasn't based in Typescript, and now that has gone away.

  • Compiler Module - The compilation process is now completely decoupled from the underlying templating framework.  This allows for all the compilation dependencies to be safely isolated as dev dependencies, but also has the added benefit of cleanly allowing future email templating frameworks to be used as well.

    In addition to the structural change all the CLI tooling has been updated for emails, including the abillity to trigger email sending from the command line for testing purposes.  Previously all the commands relied on the resource key of the email template, which was confusing to say the least.  Now that all emails are source code, the file path is all that is needed for execution.

Rest Client Support (separate from Openapi Generator)

Until recently, the only way to generate client code was using a custom Openapi code generator and leveraging the openapi.yaml generation provided by the Openapi module.  While useful, and providing a huge array of output formats, left a lot to be desired in the performance arena.  This release sees the introduction of a new module, Rest Client, that enables fast, clean, and simplified client generations specifically for Typescript ecosystems. 

  • Tighter Integration with Code Change Detection - The main goal here is to piggyback on the class/file change detection that already exists, and leverage it to produce a minimal client.  
  • Better Usage of Native Typescript Primitives -  This provides the ability to have full control over the output versus depending on whatever design decisions the Openapi Generator codebase has.  This means tighter integation on Typescript features, and less bulky generation processes.  
  • Optimized Output - The output now produces far fewer files, adds zero additional file watches, and responds much faster to changes in the rest/model schemas.
  • Multiple Outputs with a Single Project - Additionally, the code generation runs in process with the containing code, and is able to produce multiple outputs with a single process.  This opens the door for generating node-based clients, as well as web-based clients simultaneously.
  • Initial Supported Formats -
    • Fetch + Node - A node-based fetch-backed implementation
    • Fetch + Web - A web-based fetch-backed implementation
    • Angular 15+ - An angular-backed implementation

Native Fetch

Throughout the framework, there has been a perpetual need for making HTTP requests, but generally in the realm of testing and verification.  With the advent of the new RestClient module, there is now a tighter integration with crafting and invoking HTTP requests.  As of Node v18, the Fetch client is baked into the runtime, and is globally accessible (providing parity with Web-based environments).  While still an experimental feature, it is well on its way to achieving stability.  To that end, all references to node-fetch have been removed throughout the framework, and the framework's minimum node version is now bumpted to v18.  

Model + Query Enhancements:

  • Inheritance Standardization - Previously Model  and Schema approached the concept of base classes and differentiation slightly differently, even though Model depended on Schema.  This latest round of changes has standardized these patterns providing consistent understanding of how that is modeled within the framework.
  • Atomic Update Support - Within the Model query module, there is now an atomic update operation, that relies on a query to match before the update is applied.  This is very useful for optimistic locking based on a field value (e.g. a version key).   
  • Existance Checks and Empty Arrays - The Model query module now supports the ability to filter elements based on whether or not an array is empty or not.  Some model providers do not store empty arrays where others do.  This helps to standardize the behavior, treating empty arrays as not existing.
  • General Fixups - File, Memory and S3 all received various bug fixes related to stability and standardization of behavior.

Auth Cleanup

  • Auth JWT Session-Like Support - The Auth JWT module now allows for auto regenerating a new token depending on how close the token is to expiration.  When combined with the cookie encoder, this provides a mode in which the user will continue to renew the token, providing a semblance of the session timeout/renewal.
  • Removal of Auth Rest Context - This functionality is now basked into Auth-Rest

Miscellaneous Enhancements

  • Upgraded all dependencies to latest versions
  • Custom Rollup Support - Allow for complete control of the rollup behavior, providing similar functionality to that of specifying/controlling the entire docker file.
  • Standardization of all Query-based complex types (e.g. schema-backed objects mapped to query params) - We now have consistency between OpenAPI, Rest, and Rest-Client on how these types will be handled, and how prefixing of values will apply.

Wednesday, March 29, 2023

3.1.0 - Momentum

Hot on the heels of the 3.0.0 major rewrite come some major changes, along with a whole host of quality of life improvements.

What has changed?

Major/Breaking Changes

  • Upgrade Typescript to 5.0.  This is part of the ongoing progress and preparation for ES Decorators rolling out for every touch point (waiting on Parameters)
  • CLI / App Overhaul.  This is a breaking changes that most people will not notice.  The TL;DR is that @travetto/app is no more, and it's functionality has been merged into @travetto/cli.  This means that instead of making custom @App entry points, you would create a new CLI target using the @CliCommand decorator.  This also means that custom CLI commands creation has been simplified as the module now handles binding cli command flags to the class with the full support of the @travetto/schema functionality.  This also applies to the main command args.  The end result is that the sum of both modules is now greater than their individual parts.  
  • CLI / Repo Tooling Certain commands built into the @travetto/cli module were monorepo specific and the naming did not reflect that.  These commands (`exec`, `version`, `list`) have all been moved to the @travetto/repo module and are now prefixed with `repo:`, (`repo:exec`, `repo:version`, `repo:list`).   Additionally, the Cli is now invokable from sub folders where a package.json is not available. 
  • Compiler Cleanup. The new compiler introduced in 3.0.0 is fantastic, but ran into some scenarios in which some processes were continuing to run, even after the parent compiler process exited. In conjunction with stability changes, the compiler was also modified to expose progress via an IPC channel if desired.  This allows external tools to better integrate with the compiler as well.  Additionally, the terminal package received certain enhancements that better deal with narrow terminals, as well as scenarios that CTRL+C was not working properly.
  • VSCode Upgrades.  The plugin has been upgraded in line with all the above changes (App/Cli and Compiler upgrades).  This should result in a better experience, along with more control and feedback on asynch processes.
  • Manifest/Pack isolation. Pack now produces less files, and the manifest has been enhanced to contain additional context needed.  Package.json files will no longer be read at runtime (only during manifesting).  The result here is a more portable output, and the removal of node_modules in the output format.
  • DI Integration with 3rd Party Code. The di module now supports the ability to declare and inject third-party code as dependencies.  There are some limitations with code that is not framework-first, but in general this makes it much easier to integrate libraries and declare injectable components.

Whats Next?

The code will run through a few minor releases for stability, but hopefully this release can sit for a while as this is has been a pretty substantial investment.  That being said, the short list of what's next are still in line with the previous release's future looking goals:

Heap Snapshotting

With the ESM support on the horizon (and the impact it has on unloading modules at runtime), there is real impact on the testing framework performance. With the ongoing work for heap snapshot support in V8/NodeJS, this opens the door for minimizing the performance issues, while dramatically simplifying the testing/loading code.  

ES Decorators

As stated above, the expectation is that the full ES Decorator proposal will land by end of this year.   This should be a pretty minor change within the framework, but will produce much cleaner output with much less dependency on tslib. With the full ES Decorator propsal implemented, the framework will also be able to leverage functions as first class citizens.  This should allow for less code in situtations where the wrapping class was unnecessary.

Saturday, February 25, 2023

3.0.0 - Internal Overhaul

It has been over year since the last major release, and in the meantime a whole host of improvements have landed with some some breaking changes along the way.  The general theme of this work has been to reduce edge cases, and to align with external standards where possible.  The average application will see very little impact to existing code bases, as the changes tend to impact the core of the framework more than anything else.

What has changed?

Breaking Changes

  • Compilation has been reworked from the ground up. This has been one of the biggest changes in this release, and has resulted in some key benefits, primarily support for NPM and Yarn Workspaces, proper support for Ecmascript Modules, and integration with Rollup for fully tree-shakeable builds.  An added benefit is that the Travetto Github repo is now the first consumer of the monorepo support.  This also has the addede benefit of providing better integration with the VS Code plugin.  The new compiler now helps to orchestrate access across multiple invocations to ensure only one build is running at a time.
  • Logging has moved to Dependency Injection. The standard logging support will continue to work without change, but custom loggers are now provided via the Dependency Injection system.  This allows for complex logging solutions that can leverage existing services at runtime.  This also means services can now inject specific loggers as needed and leverage multiple loggers as needed.
  • Config has moved to Dependency Injection.  While the general functionality of configurations is mainly unchanged, the Configuration module now supports providing configuration content via the Dependency Injection system.  This means remote/external/complex configurations can now be used for configuring services and runtime behavior. 
    A true breaking change for Config is that it no longer attempts to map all environment variables into the configuration data.  This was rife with accuracy and security concerns and so it has been dropped in favor of a new field decorator.  The new decorator, @EnvVar("PROP"), allows for mapping specific environment variables with explicit understanding and execution.  
  • Extensions within Modules have been externalized.  This was a hard requirement for the new compiler, as the compiler assumes idemptotent compilation, and the extension system relied on file-level directives that could change depending on the dependency tree for a given module.  In lieu of the in-module extensions, the affected code has now been externalized to explicit modules:
    • Auth-model - Holds model for auth persistence with the model framework
    • Auth-rest-jwt - Support for auth-rest and jwt tokens
    • Auth-rest-session - Support for auth-rest and session integration
    • Auth-rest-context - Support for exposing the auth user into the request context support
    • Auth-passport - Clear support for passport, and handles pulling the correct dependencies
    • Email-nodemailer - Clear support for nodemailer, and handles pulling the correct dependencies
    • Rest-aws-lambda - Extracted all aws lambda code and dependencies, and can be pulled in as needed
    • Rest-express-lambda - rest-express + rest-aws-lambda + necessary deps
    • Rest-koa-lambda - rest-koa + rest-aws-lambda + necessary deps
    • Rest-fastify-lambda - rest-fastify + rest-aws-lambda + necessary deps
    • Schema-faker - Clear support for faker, and handles pulling the correct dependencies

    This also has the added benefit of removing most of the external peer dependencies, so by installing one of the modules listed above, you will get the appropriate dependencies without explicit knowledge of the underlying implementation.  
  • Resources Overhaul. With the new compilation, and support for rollup, a hard requirement is that the runtime of programs needs to assume as little as possible about direct filesystem access. In honoring that, the Resource patterns have been simplified, and no longer acts a global registry of various file searches.  It is now an opt-in pattern, and is constrained as much as possible. For the consuming services, this has resulted in no visible changes in features, but has provided stronger guarantees around runtime behavior.
  • VS Code Extension.  The VS Code extension for Travetto has been reworked from the ground up to work with all the changes listed above, and specifically the compiler changes.  The latest version of the extension is no longer compatible with the 2.x.x framework line.  Functionally the plugin should appear nearly identical, and behave the same, with a perceived performance boost.
  • Pack + Rollup.  The pack module has been completely gutted, and is only similar in name at this point.  The general usage patterns should be equivalent, but the underlying ouptut, operations, and configuration options are completely changed.  The added benefit here is that with Rollup being a known/standard tool, the output quality should improve with far less maintenance overhead.
  • JSX Support. As a general step forward, the framework now supports transpiling .tsx files.  The compiler makes no assumptions about the JSX runtime that will be used, but is now able to tie into Typescripts general support for JSX.
    • The Doc module (gnerally used internally) and all framework documentation have been moved over to JSX, using a simplistic runtime to support the documentation module.  

Major Changes

  • Rest Interceptors and Configuration. With this release, it is now possible to configuration interceptor behavior on a global and per route pattern, including fully enabling and disabling. This pattern is now standardized, and is supported by all built in interceptors.
  • Typescript 4.9+. The framework has moved to Typescript 4.9 with a strong eye towards the 5.0 release.  The new ES Decorator proposal is incoming, but is still lacking parameter support which will be a blocking feature from switching.  Typescript 5.3 will most likely provide the necessary functionality for switching away from the legacy decorator functionality.
  • ESLint as a module. There is now a module for integrating with ESLint and it's new flat configuration mechanism.  Additionally this module provides the ability to write your own lint rules in within your codebase.
  • Mono Repo support. As noted above, yarn/npm workspaces are now fully supported, and the framework is the primary dogfooder of that logic.  Additionally there is a new module '@travetto/mono-repo' that provides support for versioning and publishing mono repo projects.  
  • Ecmascript Module support. ESM support is now fully baked into the framework, including proper support in the pack module.  Currently the only caveat within the framework is the hot-reloading functionality rely on import hooks that are only well supported in CommonJS.  There is active work within Node to shore up the API for ESM module hooking, and when that lands ESM should have complete parity with CommonJS.
  • Enhanced Terminal Support. The framework has embraced the ability to interact with terminals, including progress information (streaming output, progress status, etc). Additionally the module has provided better color integration with the terminal including different color depths, and detection of light vs dark backgrounds.
  • Compiler/Build/VSCode Logging. One of the stronger quality of life improvements within this release is improved logging for the various tooling touch points.  The compiler and the vscode extension now provides structured and more detailed logs.  This also provides a solid foundation for building higher quality bug reports.
  • Test Streaming. Tests now support a new output format 'tap-streamed' which will show a progress bar of the entire test run, and will show error/output information above the progress bar as needed.  This is another nice quality of life improvement to allow a more summary-oriented test format.
  • Test Execution. Previously test execution relied on unloading code at runtime to minimize latency on running tests quickly.  This code was extremely brittle, and incompatible with ESM.  This code has been completely dropped and now the test runner will spawn a new instance on each execution.  There may be room for improvement, but the general testing performance is unchanged, and is actually more stable due to the decreased complexity in execution.

Whats Next?

The code will run through a few minor releases for stability, but hopefully this release can sit for a while as this is has been a pretty substantial investment.  That being said, the short list of what's next are still in line with the previous release's future looking goals:

Heap Snapshotting

With the ESM support on the horizon (and the impact it has on unloading modules at runtime), there is real impact on the testing framework performance. With the ongoing work for heap snapshot support in V8/NodeJS, this opens the door for minimizing the performance issues, while dramatically simplifying the testing/loading code.  

ES Decorators

As stated above, the expectation is that the full ES Decorator proposal will land by end of this year.   This should be a pretty minor change within the framework, but will produce much cleaner output with much less dependency on tslib. With the full ES Decorator propsal implemented, the framework will also be able to leverage functions as first class citizens.  This should allow for less code in situtations where the wrapping class was unnecessary.