Artus Core

repository·master·Indexed 42 days ago

https://github.com/artusjs/core

The central TypeScript package of the Artus framework. It provides core functionality including the ConfigurationHandler for multi-environment settings, LifecycleManager for application hooks (such as configWillLoad and didReady), a loader system via LoaderFactory and BaseLoader, and a dependency injection system using the artus# prefix.

Tokens
8.1K
Snippets
35
Records
40
Agent score
53%

What's inside Artus

  1. Build @artus/core from source

    master

    Since @artus/core is written in TypeScript, you must build the source code into Node.js modules before use. You can use npm, yarn, or pnpm to execute the build script.

    npm run build
    # Or
    yarn build
    # Or
    pnpm run build
  2. Configure plugin environments with PluginConfigEnvMap

    master

    Plugin configurations can be mapped by environment. A PluginConfigEnvMap is a record where the keys are environment names (e.g., 'development', 'production') and the values are the corresponding PluginConfig objects.

    // Key: Env => PluginName => Value: PluginConfigItem
    type PluginConfigEnvMap = Record<string, PluginConfig>;
  3. Map exception identifiers to filters

    master

    Artus uses an ExceptionFilterMapType to associate specific error types with their corresponding handlers. An ExceptionIdentifier can be a string, a symbol, or a Constructable<Error> (the error class itself). This allows you to target specific error classes or use unique identifiers for custom error logic.

    import { ExceptionFilterMapType, ExceptionFilterType } from '@artus/core/exception';
    
    // Example of a map associating an Error class with a filter implementation
    const filterMap: ExceptionFilterMapType = new Map([
      [TypeError, MyTypeErrorFilterClass],
      ['MY_CUSTOM_ERROR_ID', MyCustomFilterClass]
    ]);
  4. How configuration merging works

    master

    Artus uses a hierarchical merging strategy to resolve the final configuration:

    1. Default Config: It starts with the configuration stored under the ARTUS_DEFAULT_CONFIG_ENV.DEFAULT key.
    2. Environment List: It identifies the active environments. It first checks the EnvList provided via the dependency injection container. If not found, it checks the ARTUS_SERVER_ENV environment variable. If that is also missing, it defaults to ARTUS_DEFAULT_CONFIG_ENV.DEV.
    3. Merging: It iterates through the active environment list and merges each environment's configuration onto the default configuration using mergeConfig.

    This allows you to define base settings in a default file and override them specifically for dev, test, or prod environments.

  5. Configure ScannerOptions for scanning projects

    master

    When initializing a scanner, use the ScannerOptions object to define how the file system is traversed and how files are processed. Key configuration properties include:

    • extensions: An array of file extensions to include in the scan.
    • needWriteFile: Boolean indicating if the scanner should write files to the disk.
    • manifestFilePath: Optional path to a specific manifest file.
    • useRelativePath: Boolean to determine if paths should be resolved relatively.
    • exclude: An array of patterns or paths to exclude from the scan.
    • configDir: The directory containing configuration files.
    • policy: A ScanPolicy determining the scanning behavior.
    • envs: Optional array of environment variable names to consider.
    • plugin: An optional PluginConfig to extend scanner behavior.
    • app: An optional Application instance.
    const options: ScannerOptions = {
      extensions: ['.ts', '.js'],
      needWriteFile: true,
      configDir: './config',
      policy: ScanPolicy.SOME_POLICY, // Import ScanPolicy from '../constant'
      exclude: ['node_modules', 'dist'],
      useRelativePath: true
    };
  6. Configure logger settings with LoggerOptions

    master

    When initializing or configuring a logger in Artus, you can provide a LoggerOptions object to specify the minimum logging threshold. The level property determines which log messages are processed based on their severity.

    const options: LoggerOptions = {
      level: 'info' // or any valid LoggerLevel
    };
  7. Define a Manifest structure

    master

    A Manifest is the central configuration object used by the loader. It must specify a version of '2' and contains a refMap which maps reference names to their respective items. You can optionally provide extraPluginConfig to supply additional configuration to plugins.

    interface Manifest {
      version: '2';
      refMap: RefMap;
      extraPluginConfig?: PluginConfig;
    }
  8. Use the Application interface to manage the application lifecycle

    master

    The Application interface is the central entry point for managing an Artus application. It provides access to core services like the dependency injection container, configurationHandler, lifecycleManager, loaderFactory, and logger.

    To initialize and start an application, you typically call .load(manifest) followed by .run(). You can also intercept various stages of the application lifecycle by registering hooks using .registerHook(hookName, hookFn).

    // Conceptual usage of the Application interface
    const app: Application = // ... initialized application instance
    
    await app.load(myManifest);
    await app.run();
    
    app.registerHook('didReady', () => {
      console.log('Application is ready!');
    });
  9. Use LifecycleManager to manage application lifecycle hooks

    master

    The LifecycleManager class is a singleton service used to manage and trigger application lifecycle events. It allows you to register custom hook functions or register classes that use metadata to automatically bind lifecycle methods.

    Available Default Hooks

    The manager maintains a predefined sequence of hooks:

    • configWillLoad: Triggered just before configuration files are loaded. Use this to dynamically modify configuration.
    • configDidLoad: Triggered after configuration files are loaded.
    • didLoad: Triggered after files are loaded.
    • willReady: Triggered when plugins have finished starting.
    • didReady: Triggered when the application has fully started.
    • beforeClose: Triggered just before the application shuts down.

    Registering Hooks

    You can register hooks in two ways:

    1. Manual Registration: Use registerHook(hookName, hookFn) to attach a function to a specific hook name.
    2. Class-based Registration: Use registerHookUnit(extClazz) on a class. If the class methods are decorated with metadata starting with the HOOK_NAME_META_PREFIX (internal constant), the LifecycleManager will automatically extract and bind those methods to the corresponding lifecycle hooks.

    Emitting Hooks

    Use emitHook(hookName, payload, reverse) to trigger the registered functions.

    • Note: Lifecycle hooks are designed to trigger only once. Once emitHook is called for a specific hookName, that hook is removed from the internal map.
    • Payload: You can pass an optional payload of any type to the hook functions.
    • Reverse Execution: Setting reverse: true will execute the registered functions in reverse order.
    // Example: Manual hook registration
    lifecycleManager.registerHook('didReady', async ({ app, payload }) => {
      console.log('Application is ready with payload:', payload);
    });
    
    // Triggering the hook
    await lifecycleManager.emitHook('didReady', { status: 'ok' });
  10. Use the Scanner API to generate a Manifest

    master

    The ScannerType interface defines the primary method for performing a scan. A scanner instance is typically created via a ScannerConstructor and provides a scan method that returns a Manifest representing the scanned project structure.

    // Assuming Scanner is the implementation of ScannerConstructor
    const scanner = new Scanner({ 
      extensions: ['.ts'],
      configDir: './config',
      policy: ScanPolicy.DEFAULT
    });
    
    // Perform the scan on a specific root directory
    const manifest = await scanner.scan('./my-project-root');
  11. Manipulate the lifecycle hook sequence

    master

    The LifecycleManager provides methods to modify the order of the lifecycle execution sequence:

    • insertHook(existHookName: string, newHookName: string): Inserts a newHookName immediately after the existHookName in the execution sequence.
    • appendHook(newHookName: string): Adds a newHookName to the end of the execution sequence.