UNPKG

alepha

Version:

Alepha is a convention-driven TypeScript framework for building robust, end-to-end type-safe applications, from serverless APIs to full-stack React apps.

1,456 lines (1,450 loc) 45.4 kB
import { AsyncLocalStorage } from "node:async_hooks"; import { TypeCheck } from "@sinclair/typebox/compiler"; import * as TypeBoxValue from "@sinclair/typebox/value"; import * as TypeBox from "@sinclair/typebox"; import { ArrayOptions, FormatRegistry, IntegerOptions, NumberOptions, ObjectOptions, SchemaOptions, Static, Static as Static$1, StaticDecode, StaticEncode, StringOptions, TAny, TAny as TAny$1, TArray, TArray as TArray$1, TBoolean, TBoolean as TBoolean$1, TComposite, TInteger, TIntersect, TNull, TNumber, TNumber as TNumber$1, TObject, TObject as TObject$1, TOmit, TOptional, TOptionalWithFlag, TPartial, TPick, TProperties, TProperties as TProperties$1, TRecord, TRecord as TRecord$1, TSchema, TSchema as TSchema$1, TString, TString as TString$1, TUnion, TUnsafe, TypeGuard, UnsafeOptions } from "@sinclair/typebox"; import { ValueError } from "@sinclair/typebox/errors"; import { Readable } from "node:stream"; import { ReadableStream as ReadableStream$1 } from "node:stream/web"; //#region src/constants/KIND.d.ts /** * Used for identifying descriptors. * * @internal */ declare const KIND: unique symbol; //# sourceMappingURL=KIND.d.ts.map //#endregion //#region src/constants/MODULE.d.ts /** * Used for identifying modules. * * @internal */ declare const MODULE: unique symbol; //# sourceMappingURL=MODULE.d.ts.map //#endregion //#region src/constants/OPTIONS.d.ts /** * Used for descriptors options. * * @internal */ declare const OPTIONS: unique symbol; //# sourceMappingURL=OPTIONS.d.ts.map //#endregion //#region src/interfaces/Service.d.ts /** * In Alepha, a service is a class that can be instantiated or an abstract class. Nothing more, nothing less... */ type Service<T extends object = any> = InstantiableClass<T> | AbstractClass<T>; type InstantiableClass<T extends object = any> = new (...args: any[]) => T; /** * Abstract class is a class that cannot be instantiated directly! * It widely used for defining interfaces. */ type AbstractClass<T extends object = any> = abstract new (...args: any[]) => T; /** * Service substitution allows you to register a class as a different class. * Providing class A, but using class B instead. * This is useful for testing, mocking, or providing a different implementation of a service. * * class A is mostly an AbstractClass, while class B is an InstantiableClass. */ interface ServiceSubstitution<T extends object = any> { /** * Every time someone asks for this class, it will be provided with the 'use' class. */ provide: Service<T>; /** * Service to use instead of the 'provide' service. * * Syntax is inspired by Angular's DI system. */ use: Service<T>; /** * If true, if the service already exists -> just ignore the substitution and do not throw an error. * Mostly used for plugins to enforce a substitution without throwing an error. */ optional?: boolean; } /** * Every time you register a service, you can use this type to define it. * * alepha.with( ServiceEntry ) * or * alepha.with( provide: ServiceEntry, use: MyOwnServiceEntry ) * * And yes, you declare the *type* of the service, not the *instance*. */ type ServiceEntry<T extends object = any> = Service<T> | ServiceSubstitution<T>; //# sourceMappingURL=Service.d.ts.map //#endregion //#region src/helpers/descriptor.d.ts interface DescriptorArgs<T extends object = {}> { options: T; alepha: Alepha; service: InstantiableClass<Service>; module?: Service; } interface DescriptorConfig { propertyKey: string; service: InstantiableClass<Service>; module?: Service; } declare abstract class Descriptor<T extends object = {}> { protected readonly alepha: Alepha; readonly options: T; readonly config: DescriptorConfig; constructor(args: DescriptorArgs<T>); /** * Called automatically by Alepha after the descriptor is created. */ protected onInit(): void; } type DescriptorFactory<TDescriptor extends Descriptor = Descriptor> = { (options: TDescriptor["options"]): TDescriptor; [KIND]: InstantiableClass<TDescriptor>; }; type DescriptorFactoryLike<T extends object = any> = { (options: T): any; [KIND]: any; }; declare const createDescriptor: <TDescriptor extends Descriptor>(descriptor: InstantiableClass<TDescriptor> & { [MODULE]?: Service; }, options: TDescriptor["options"]) => TDescriptor; //# sourceMappingURL=descriptor.d.ts.map //#endregion //#region src/descriptors/$module.d.ts /** * Wrap services and descriptors into a module. * * Module is just a class. * You must attach a `name` to it. * * It's recommended to use `project.module.submodule` format. * * @example * ```ts * import { $module } from "alepha"; * import { MyService } from "./MyService.ts"; * * // export MyService so it can be used everywhere * export * from "./MyService.ts"; * * export default $module({ * name: "my.project.module", * // MyService will have a module context "my.project.module" * services: [MyService], * }); * ``` * * - Module is used for logging and other purposes. * - It's useful for large applications or libraries to group services and descriptors together. * - It's probably overkill for small applications. */ declare const $module: (options: ModuleDescriptorOptions) => Service<Module>; interface ModuleDescriptorOptions { /** * Name of the module. * * It should be in the format of `project.module.submodule`. */ name: string; /** * List of services to register in the module. */ services?: Array<Service>; /** * List of $descriptors to register in the module. */ descriptors?: Array<DescriptorFactoryLike>; /** * By default, module will register all services. * You can override this behavior by providing a register function. * It's useful when you want to register services conditionally or in a specific order. */ register?: (alepha: Alepha) => void; } interface Module { [KIND]: "MODULE"; [OPTIONS]: ModuleDescriptorOptions; register: (alepha: Alepha) => void; } type ServiceWithModule<T extends object = any> = T & { [MODULE]?: Service; }; declare const isModule: (value: unknown) => value is Module; declare const toModuleName: (name: string) => string; //# sourceMappingURL=$module.d.ts.map //#endregion //#region src/interfaces/Async.d.ts /** * Represents a value that can be either a value or a promise of value. */ type Async<T> = T | Promise<T>; /** * Represents a function that returns an async value. */ type AsyncFn = (...args: any[]) => Async<any>; /** * Transforms a type T into a promise if it is not already a promise. */ type MaybePromise<T> = T extends Promise<any> ? T : Promise<T>; //# sourceMappingURL=Async.d.ts.map //#endregion //#region src/providers/AlsProvider.d.ts type AsyncLocalStorageData = any; declare class AlsProvider { static create: () => AsyncLocalStorage<AsyncLocalStorageData> | undefined; als?: AsyncLocalStorage<AsyncLocalStorageData>; constructor(); createContextId(): string; run<R>(callback: () => R, data?: Record<string, any>): R; exists(): boolean; get<T>(key: string): T | undefined; set<T>(key: string, value: T): void; } //# sourceMappingURL=AlsProvider.d.ts.map //#endregion //#region src/services/Logger.d.ts type LogLevel = "error" | "warn" | "info" | "debug" | "trace" | "silent"; interface LoggerEnv { /** * Default log level for the application. * Default by environment: * - dev = "debug" * - test = "error" * - prod = "info" * * "trace" | "debug" | "info" | "warn" | "error" | "silent" */ LOG_LEVEL?: string; /** * Disable colors in the console output. */ NO_COLOR?: string; /** * Force color output for the application. */ FORCE_COLOR?: string; /** * Log format. * * @default "text" */ LOG_FORMAT?: "json" | "text" | "cli" | "raw"; } interface LoggerOptions { /** * The logging level. Can be one of "error", "warn", "info", "debug", or "trace". */ level?: string; /** * The name of the application. Used to identify the source of the log messages. */ app?: string; /** * The name of the logger. Like a module name or a service name. */ name?: string; /** * An optional context to include in the log output. Like a request ID or a correlation ID. */ context?: string; /** * An optional tag to include in the log output. Like a class name or a module name. */ caller?: string; /** * Whether to use colors in the log output. Defaults to true. */ color?: boolean; /** * Log output format. Can be "json", "text", or "cli". */ format?: string; /** * An optional async local storage provider to use for storing context information. */ als?: AlsProvider; } declare const COLORS: { reset: string; grey: string; red: string; orange: string; green: string; blue: string; white: string; cyan: string; darkGrey: string; }; declare const LEVEL_COLORS: Record<string, string>; declare class Logger { protected levelOrder: Record<string, number>; readonly level: string; readonly rawLevel: string; readonly name: string; protected caller: string; protected context: string; protected app: string; protected color: boolean; protected format: string; protected als?: AlsProvider; constructor(options?: LoggerOptions); parseLevel(level: string, app: string): LogLevel; asLogLevel(something: string): LogLevel; child(options: LoggerOptions): Logger; error(message: unknown, data?: object | Error | string | unknown): void; warn(message: unknown, data?: object | Error | string | unknown): void; info(message: unknown, data?: object | Error | string): void; debug(message: unknown, data?: object | Error | string): void; trace(message: unknown, data?: object | Error | string): void; /** * Log a message to the console. */ protected log(level: LogLevel, message: unknown, data?: object | Error | string): void; /** * Print a log message to the console. */ protected print(formatted: string): void; /** * Format a log message to JSON. */ protected formatJson(level: LogLevel, message: unknown, data?: object | Error | string): string; protected formatJsonError(error: Error): object; /** * Format a log message to a string. */ protected formatLog(level: LogLevel, message: string, data?: object | Error): string; protected nowTimeFormatted(): string; protected pad2: (n: number) => string; protected pad3: (n: number) => string; protected colorize(color: string, text: string, reset?: string): string; /** * Format an error to a string. * * @param error * @protected */ protected formatError(error: Error): string; } declare class MockLogger extends Logger { store: MockLoggerStore; constructor(options?: LoggerOptions & { store: MockLoggerStore; }); print(msg: string): void; child(options: LoggerOptions): MockLogger; reset(): void; } interface MockLoggerStore { stack: Array<{ date: string; level: string; message: string; context?: string; app?: string; } & Record<string, any>>; } //# sourceMappingURL=Logger.d.ts.map //#endregion //#region src/Alepha.d.ts /** * Core container of the Alepha framework. * * It is responsible for managing the lifecycle of services, * handling dependency injection, * and providing a unified interface for the application. * * @example * ```ts * import { Alepha, run } from "alepha"; * * class MyService { * // business logic here * } * * const alepha = Alepha.create({ * // state, env, and other properties * }) * * alepha.with(MyService); * * run(alepha); // trigger .start (and .stop) automatically * ``` * * ### Alepha Factory * * Alepha.create() is an enhanced version of new Alepha(). * - It merges `process.env` with the provided state.env when available. * - It populates the test hooks for Vitest or Jest environments when available. * * new Alepha() is fine if you don't need these helpers. * * ### Platforms & Environments * * Alepha is designed to work in various environments: * - **Browser**: Runs in the browser, using the global `window` object. * - **Serverless**: Runs in serverless environments like Vercel or Vite. * - **Test**: Runs in test environments like Jest or Vitest. * - **Production**: Runs in production environments, typically with NODE_ENV set to "production". * * You can check the current environment using the following methods: * * - `isBrowser()`: Returns true if the App is running in a browser environment. * - `isServerless()`: Returns true if the App is running in a serverless environment. * - `isTest()`: Returns true if the App is running in a test environment. * - `isProduction()`: Returns true if the App is running in a production environment. * * ### State & Environment * * The state of the Alepha container is stored in the `store` property. * Most important property is `store.env`, which contains the environment variables. * * ```ts * const alepha = Alepha.create({ env: { MY_VAR: "value" } }); * * // You can access the environment variables using alepha.env * console.log(alepha.env.MY_VAR); // "value" * * // But you should use $env() descriptor to get typed values from the environment. * class App { * env = $env( * t.object({ * MY_VAR: t.string(), * }) * ); * } * ``` * * ### Modules * * Modules are a way to group services together. * You can register a module using the `$module` descriptor. * * ```ts * import { $module } from "alepha"; * * class MyLib {} * * const myModule = $module({ * name: "my.project.module", * services: [MyLib], * }); * ``` * * Do not use modules for small applications. * * ### Hooks * * Hooks are a way to run async functions from all registered providers/services. * You can register a hook using the `$hook` descriptor. * * ```ts * import { $hook } from "alepha"; * * class App { * log = $logger(); * onCustomerHook = $hook({ * on: "my:custom:hook", * handler: () => { * this.log.info("App is being configured"); * }, * }); * } * * Alepha.create() * .with(App) * .start() * .then(alepha => alepha.emit("my:custom:hook")); * ``` * * Hooks are fully typed. You can create your own hooks by using module augmentation: * * ```ts * declare module "alepha" { * interface Hooks { * "my:custom:hook": { * arg1: string; * } * } * } * ``` */ declare class Alepha { /** * Creates a new instance of the Alepha container with some helpers: * * - merges `process.env` with the provided state.env when available. * - populates the test hooks for Vitest or Jest environments when available. * * If you are not interested about these helpers, you can use the constructor directly. */ static create(state?: Partial<State>): Alepha; /** * List of all services + how they are provided. */ protected registry: Map<Service, ServiceDefinition>; /** * Flag indicating whether the App won't accept any further changes. * Pass to true when #start() is called. */ protected locked: boolean; /** * True if the App has been configured. */ protected configured: boolean; /** * True if the App has started. */ protected started: boolean; /** * True if the App is ready. */ protected ready: boolean; /** * A promise that resolves when the App has started. */ protected starting?: PromiseWithResolvers<this>; /** * The current state of the App. * * It contains the environment variables, logger, and other state-related properties. * * You can declare your own state properties by extending the `State` interface. * * ```ts * declare module "alepha" { * interface State { * myCustomValue: string; * } * } * ``` * * Same story for the `Env` interface. * ```ts * declare module "alepha" { * interface Env { * readonly myCustomValue: string; * } * } * ``` * * State values can be function or primitive values. * However, all .env variables must serializable to JSON. */ protected store: State; /** * During the instantiation process, we keep a list of pending instantiations. * > It allows us to detect circular dependencies. */ protected pendingInstantiations: Service[]; /** * Cache for environment variables. * > It allows us to avoid parsing the same schema multiple times. */ protected cacheEnv: Map<TSchema$1, any>; /** * Cache for TypeBox type checks. * > It allows us to avoid compiling the same schema multiple times. */ protected cacheTypeCheck: Map<TSchema$1, TypeCheck<TSchema$1>>; /** * List of events that can be triggered. Powered by $hook(). */ protected events: Record<string, Array<Hook>>; /** * List of modules that are registered in the container. * * Modules are used to group services and provide a way to register them in the container. */ protected modules: Array<Module>; protected substitutions: Map<Service, { use: Service; }>; protected configurations: Map<Service, object>; protected descriptorRegistry: Map<Service<Descriptor<{}>>, Descriptor<{}>[]>; /** * Node.js feature that allows to store context across asynchronous calls. * * This is used for logging, tracing, and other context-related features. * * Mocked for browser environments. */ readonly context: AlsProvider; /** * Get logger instance. */ get log(): Logger; /** * The environment variables for the App. */ get env(): Readonly<Env>; constructor(state?: Partial<State>); /** * State accessor and mutator. */ state<Key extends keyof State>(key: Key, value?: State[Key]): State[Key]; /** * True when start() is called. * * -> No more services can be added, it's over, bye! */ isLocked(): boolean; /** * Returns whether the App is configured. * * It means that Alepha#configure() has been called. * * > By default, configure() is called automatically when start() is called, but you can also call it manually. */ isConfigured(): boolean; /** * Returns whether the App has started. * * It means that #start() has been called but maybe not all services are ready. */ isStarted(): boolean; /** * True if the App is ready. It means that Alepha is started AND ready() hook has beed called. */ isReady(): boolean; /** * True if the App is running in a browser environment. */ isBrowser(): boolean; /** * Returns whether the App is running in a serverless environment. * * > Vite developer mode is also considered serverless. */ isServerless(): boolean | "vite" | "vercel"; /** * Returns whether the App is in test mode. (Running in a test environment) * * > This is automatically set when running tests with Jest or Vitest. */ isTest(): boolean; /** * Returns whether the App is in production mode. (Running in a production environment) * * > This is automatically set by Vite or Vercel. However, you have to set it manually when running Docker apps. */ isProduction(): boolean; /** * Starts the App. * * - Lock any further changes to the container. * - Run "configure" hook for all services. Descriptors will be processed. * - Run "start" hook for all services. Providers will connect/listen/... * - Run "ready" hook for all services. This is the point where the App is ready to serve requests. * * @return A promise that resolves when the App has started. */ start(): Promise<this>; /** * Stops the App. * * - Run "stop" hook for all services. * * Stop will NOT reset the container. * Stop will NOT unlock the container. * * > Stop is used to gracefully shut down the application, nothing more. There is no "restart". * * @return A promise that resolves when the App has stopped. */ stop(): Promise<void>; /** * Check if entry is registered in the container. */ has(entry: ServiceEntry, opts?: { /** * Check if the entry is registered in the pending instantiation stack. * * Default: true */ inStack?: boolean; /** * Check if the entry is registered in the container registry. * * Default: true */ inRegistry?: boolean; }): boolean; /** * Registers the specified service in the container. * * - If the service is ALREADY registered, the method does nothing. * - If the service is NOT registered, a new instance is created and registered. * * Method is chainable, so you can register multiple services in a single call. * * > ServiceEntry allows to provide a service **substitution** feature. * * @example * ```ts * class A { value = "a"; } * class B { value = "b"; } * class M { a = $inject(A); } * * Alepha.create().with({ provide: A, use: B }).get(M).a.value; // "b" * ``` * * > **Substitution** is an advanced feature that allows you to replace a service with another service. * > It's useful for testing or for providing different implementations of a service. * > If you are interested in configuring a service, use Alepha#configure() instead. * * @param serviceEntry - The service to register in the container. * @return Current instance of Alepha. */ with<T extends object>(serviceEntry: ServiceEntry<T> | { default: ServiceEntry<T>; }): this; /** * Get the instance of the specified service and apply some changes, depending on the options. * - If the service is already registered, it will return the existing instance. (except if `skipCache` is true) * - If the service is not registered, it will create a new instance and register it. (except if `skipRegistration` is true) * - New instance can be created with custom constructor arguments. (`args` option) * * > This method is used by $inject() under the hood. * * @return The instance of the specified class or type. */ inject<T extends object>(service: Service<T>, opts?: { /** * Ignore current existing instance. */ skipCache?: boolean; /** * Don't store the instance in the registry. */ skipRegistration?: boolean; /** * Constructor arguments to pass when creating a new instance. */ args?: ConstructorParameters<InstantiableClass<T>>; /** * Parent service that requested the instance. * @internal */ parent?: Service | null; }): T; /** * Configures the specified service with the provided state. * If service is not registered, it will do nothing. * * It's recommended to use this method on the `configure` hook. * @example * ```ts * class AppConfig { * configure = $hook({ * name: "configure", * handler: (a) => { * a.configure(MyProvider, { some: "data" }); * } * }) * } * ``` */ configure<T extends { options: object; }>(service: Service<T>, state: Partial<T["options"]>): this; /** * Registers a hook for the specified event. */ on<T extends keyof Hooks>(event: T, hookOrFunc: Hook<T> | ((payload: Hooks[T]) => Async<void>)): () => void; /** * Emits the specified event with the given payload. */ emit<T extends keyof Hooks>(func: keyof Hooks, payload: Hooks[T], options?: { /** * If true, the hooks will be executed in reverse order. * This is useful for "stop" hooks that should be executed in reverse order. * * @default false */ reverse?: boolean; /** * If true, the hooks will be logged with their execution time. * * @default false */ log?: boolean; /** * If true, errors will be caught and logged instead of throwing. * * @default false */ catch?: boolean; }): Promise<void>; /** * Casts the given value to the specified schema. * * It uses the TypeBox library to validate the value against the schema. */ parse<T extends TSchema$1>(schema: T, value?: any, opts?: { /** * Clone the value before parsing. * @default true */ clone?: boolean; /** * Apply default values defined in the schema. * @default true */ default?: boolean; /** * Remove all values not defined in the schema. * @default true */ clean?: boolean; /** * Try to cast/convert some data based on the schema. * @default true */ convert?: boolean; /** * Prepare value after being deserialized. * @default true */ check?: boolean; }): Static$1<T>; /** * Applies environment variables to the provided schema and state object. * * It replaces also all templated $ENV inside string values. * * @param schema - The schema object to apply environment variables to. * @return The schema object with environment variables applied. */ parseEnv<T extends TObject$1>(schema: T): Static$1<T>; /** * Dump the current dependency graph of the App. * * This method returns a record where the keys are the names of the services. */ graph(): Record<string, { from: string[]; as?: string[]; module?: string; }>; descriptors<TDescriptor extends Descriptor>(factory: { [KIND]: InstantiableClass<TDescriptor> | string; }): Array<TDescriptor>; protected new<T extends object>(service: Service<T>, args?: any[]): T; protected processDescriptor(value: Descriptor, propertyKey?: string): void; /** * @internal */ protected createLogger(env: Env): Logger; } interface Hook<T extends keyof Hooks = any> { caller?: Service; priority?: "first" | "last"; callback: (payload: Hooks[T]) => Async<void>; } /** * This is how we store services in the Alepha container. */ interface ServiceDefinition<T extends object = any> { /** * The instance of the class or type definition. * Mostly used for caching / singleton but can be used for other purposes like forcing the instance. */ instance: T; /** * List of classes which use this class. */ parents: Array<Service | null>; /** * If the service is provided by a module, the module definition. */ module?: Service; } interface Env extends LoggerEnv { [key: string]: string | boolean | number | undefined; /** * Optional environment variable that indicates the current environment. */ NODE_ENV?: "dev" | "test" | "production"; /** * Optional name of the application. */ APP_NAME?: string; /** * Optional root module name. */ MODULE_NAME?: string; } interface State { log: Logger; env?: Readonly<Env>; /** * If defined, the Alepha container will only register this service and its dependencies. */ target?: Service; beforeAll?: (run: any) => any; afterAll?: (run: any) => any; afterEach?: (run: any) => any; onTestFinished?: (run: any) => any; } interface Hooks { echo: any; /** * Triggered during the configuration phase. Before the start phase. */ configure: Alepha; /** * Triggered during the start phase. When `Alepha#start()` is called. */ start: Alepha; /** * Triggered during the ready phase. After the start phase. */ ready: Alepha; /** * Triggered during the stop phase. * * - Stop should be called after a SIGINT or SIGTERM signal in order to gracefully shutdown the application. (@see `run()` method) * */ stop: Alepha; /** * Triggered when a state value is mutated. */ "state:mutate": { /** * The key of the state that was mutated. */ key: keyof State; /** * The new value of the state. */ value: any; /** * The previous value of the state. */ prevValue: any; }; } //#endregion //#region src/interfaces/Run.d.ts interface RunOptions { /** * Environment variables to be used by the application. * It will be merged with the current process environment. */ env?: Env; /** * A callback that will be executed before the application starts. */ configure?: (alepha: Alepha) => Async<void>; /** * A callback that will be executed once the application is ready. * This is useful for initializing resources or starting background tasks. */ ready?: (alepha: Alepha) => Async<void>; /** * If true, the application will .stop() after the ready callback is executed. * Useful for one-time tasks! */ once?: boolean; /** * If true, the application will run in cluster mode. */ cluster?: boolean; } //# sourceMappingURL=Run.d.ts.map //#endregion //#region src/constants/PRIMITIVE.d.ts /** * Symbol to mark a value as a primitive. * * Used to enhance TypeBox types with metadata. See @alepha/protobuf. * * @internal */ declare const PRIMITIVE: unique symbol; //# sourceMappingURL=PRIMITIVE.d.ts.map //#endregion //#region src/descriptors/$cursor.d.ts /** * Get Alepha instance and Class definition from the current context. * This should be used inside a descriptor only. * * ```ts * import { $cursor } from "alepha"; * * const $ = () => { * * const { context, definition } = $cursor(); * * // context - alepha instance * // definition - class which is creating this descriptor * * return {}; * } * * ``` * * @internal */ declare const $cursor: () => CursorDescriptor; /** * /!\ Global variable /!\ * * Store the current context and definition during injection phase. * * @internal */ declare const __alephaRef: { context?: Alepha; definition?: Service & { [MODULE]?: Service; }; parent?: Service; }; /** * Cursor descriptor. */ interface CursorDescriptor { context: Alepha; definition?: Service; module?: Service; } //# sourceMappingURL=$cursor.d.ts.map //#endregion //#region src/descriptors/$env.d.ts /** * Get typed values from environment variables. * * @example * ```ts * const alepha = Alepha.create({ * env: { * // Alepha.create() will also use process.env when running on Node.js * HELLO: "world", * } * }); * * class App { * log = $logger(); * * // program expect a var env "HELLO" as string to works * env = $env(t.object({ * HELLO: t.string() * })); * * sayHello = () => this.log.info("Hello ${this.env.HELLO}") * } * * run(alepha.with(App)); * ``` */ declare const $env: <T extends TObject$1>(type: T) => Static$1<T>; //# sourceMappingURL=$env.d.ts.map //#endregion //#region src/descriptors/$hook.d.ts /** * Registers a new hook. * * ```ts * import { $hook } from "alepha"; * * class MyProvider { * onStart = $hook({ * name: "start", // or "configure", "ready", "stop", ... * handler: async (app) => { * // await db.connect(); ... * } * }); * } * ``` * * Hooks are used to run async functions from all registered providers/services. * * You can't register a hook after the App has started. * * It's used under the hood by the `configure`, `start`, and `stop` methods. * Some modules also use hooks to run their own logic. (e.g. `@alepha/server`). * * You can create your own hooks by using module augmentation: * * ```ts * declare module "alepha" { * * interface Hooks { * "my:custom:hook": { * arg1: string; * } * } * } * * await alepha.emit("my:custom:hook", { arg1: "value" }); * ``` * */ declare const $hook: { <T extends keyof Hooks>(options: HookOptions<T>): HookDescriptor<T>; [KIND]: typeof HookDescriptor; }; interface HookOptions<T extends keyof Hooks> { /** * The name of the hook. "configure", "start", "ready", "stop", ... */ on: T; /** * The handler to run when the hook is triggered. */ handler: (app: Hooks[T]) => Async<any>; /** * Force the hook to run first or last on the list of hooks. */ priority?: "first" | "last"; /** * Empty placeholder, not implemented yet. :-) */ before?: object | Array<object>; /** * Empty placeholder, not implemented yet. :-) */ after?: object | Array<object>; } declare class HookDescriptor<T extends keyof Hooks> extends Descriptor<HookOptions<T>> { called: number; protected onInit(): void; } //# sourceMappingURL=$hook.d.ts.map //#endregion //#region src/descriptors/$inject.d.ts /** * Get the instance of the specified type from the context. * * ```ts * class A { } * class B { * a = $inject(A); * } * ``` */ declare const $inject: <T extends object>(type: Service<T>) => T; declare class InjectDescriptor extends Descriptor {} //# sourceMappingURL=$inject.d.ts.map //#endregion //#region src/descriptors/$logger.d.ts /** * Create a logger. * * `name` is optional, by default it will use the name of the service. * * @example * ```ts * import { $logger } from "alepha"; * * class MyService { * log = $logger(); * * constructor() { * // print something like 'date - [MyService] Service initialized' * this.log.info("Service initialized"); * } * } * ``` */ declare const $logger: { (options?: LoggerDescriptorOptions): Logger; [KIND]: typeof Logger; }; interface LoggerDescriptorOptions { name?: string; } //# sourceMappingURL=$logger.d.ts.map //#endregion //#region src/errors/AlephaError.d.ts /** * Default error class for Alepha. */ declare class AlephaError extends Error { name: string; } //# sourceMappingURL=AlephaError.d.ts.map //#endregion //#region src/errors/AppNotStartedError.d.ts declare class AppNotStartedError extends Error { readonly name = "AppNotStartedError"; constructor(); } //# sourceMappingURL=AppNotStartedError.d.ts.map //#endregion //#region src/errors/CircularDependencyError.d.ts declare class CircularDependencyError extends Error { readonly name = "CircularDependencyError"; constructor(provider: string, parents?: string[]); } //# sourceMappingURL=CircularDependencyError.d.ts.map //#endregion //#region src/errors/ContainerLockedError.d.ts declare class ContainerLockedError extends Error { readonly name = "ContainerLockedError"; constructor(message?: string); } //# sourceMappingURL=ContainerLockedError.d.ts.map //#endregion //#region src/errors/TypeBoxError.d.ts declare class TypeBoxError extends Error { readonly name = "TypeBoxError"; readonly value: ValueError; constructor(value: ValueError); } //# sourceMappingURL=TypeBoxError.d.ts.map //#endregion //#region src/providers/TypeProvider.d.ts declare class TypeProvider { static DEFAULT_STRING_MAX_LENGTH: number; static DEFAULT_LONG_STRING_MAX_LENGTH: number; static DEFAULT_RICH_STRING_MAX_LENGTH: number; static DEFAULT_ARRAY_MAX_ITEMS: number; static FormatRegistry: typeof FormatRegistry; raw: TypeBox.JavaScriptTypeBuilder; any: (options?: SchemaOptions) => TAny$1; void: (options?: SchemaOptions) => TypeBox.TVoid; undefined: (options?: SchemaOptions) => TypeBox.TUndefined; record: <Key extends TSchema$1, Value extends TSchema$1>(key: Key, value: Value, options?: ObjectOptions) => TypeBox.TRecordOrObject<Key, Value>; omit: { <Type extends TSchema$1, Key extends PropertyKey[]>(type: Type, key: readonly [...Key], options?: SchemaOptions): TOmit<Type, Key>; <Type extends TSchema$1, Key extends TSchema$1>(type: Type, key: Key, options?: SchemaOptions): TOmit<Type, Key>; }; partial: { <MappedResult extends TypeBox.TMappedResult>(type: MappedResult, options?: SchemaOptions): TypeBox.TPartialFromMappedResult<MappedResult>; <Type extends TSchema$1>(type: Type, options?: SchemaOptions): TPartial<Type>; }; union: <Types extends TSchema$1[]>(types: [...Types], options?: SchemaOptions) => TypeBox.Union<Types>; composite: <T extends TSchema$1[]>(schemas: [...T], options?: ObjectOptions) => TComposite<T>; pick: { <Type extends TSchema$1, Key extends PropertyKey[]>(type: Type, key: readonly [...Key], options?: SchemaOptions): TPick<Type, Key>; <Type extends TSchema$1, Key extends TSchema$1>(type: Type, key: Key, options?: SchemaOptions): TPick<Type, Key>; }; /** * Create a schema for an object. * * @param properties The properties of the object. * @param options The options for the object. */ object<T extends TProperties$1>(properties: T, options?: ObjectOptions): TObject$1<T>; /** * Create a schema for an array. * * @param schema * @param options */ array<T extends TSchema$1>(schema: T, options?: ArrayOptions): TArray$1<T>; /** * Create a schema for a string. * * @param options */ string(options?: AlephaStringOptions): TString$1; /** * Create a schema for a JSON object. * * @param options */ json(options?: SchemaOptions): TRecord$1<TString$1, TAny$1>; /** * Create a schema for a boolean. * * @param options */ boolean(options?: SchemaOptions): TBoolean$1; /** * Create a schema for a number. * * @param options */ number(options?: NumberOptions): TNumber$1; /** * Create a schema for an unsigned 8-bit integer. * * @param options */ uchar(options?: IntegerOptions): TInteger; /** * Create a schema for an unsigned 32-bit integer. */ uint(options?: IntegerOptions): TNumber$1; /** * Create a schema for a signed 32-bit integer. */ int(options?: IntegerOptions): TInteger; /** * Create a schema for a signed 32-bit integer. */ integer(options?: IntegerOptions): TInteger; /** * Create a schema for a bigint. Bigint is a 64-bit integer. * This is a workaround for TypeBox, which does not support bigint natively. */ bigint(options?: NumberOptions): TNumber$1; /** * Make a schema optional. * * @param schema The schema to make optional. */ optional<T extends TSchema$1>(schema: T): TOptionalWithFlag<T, true>; /** * Make a schema nullable. * * @param schema The schema to make nullable. * @param options The options for the schema. */ nullable<T extends TSchema$1>(schema: T, options?: ObjectOptions): TUnion<[TNull, T]>; nullify: <T extends TSchema$1>(schema: T, options?: ObjectOptions) => TObject$1<TypeBox.Evaluate<TypeBox.TMappedFunctionReturnType<TypeBox.TIndexPropertyKeys<TypeBox.TKeyOf<T>>, TUnion<[TNull, TypeBox.TMappedResult<TypeBox.Evaluate<TypeBox.TIndexPropertyKeys<TypeBox.TKeyOf<T>> extends infer T_1 ? T_1 extends TypeBox.TIndexPropertyKeys<TypeBox.TKeyOf<T>> ? T_1 extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? Right extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] ? /*elided*/any : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : { [_ in Left]: TypeBox.Assert<TypeBox.TIndexFromPropertyKey<T, Left>, TSchema$1> } : {} : never : never>>]>, {}>>>; /** * Map a schema to another schema. * * @param schema The schema to map. * @param operations The operations to perform on the schema. * @param options The options for the schema. * @returns The mapped schema. */ map<T extends TObject$1 | TIntersect, Omit extends (keyof T["properties"])[], Optional extends (keyof T["properties"])[]>(schema: T, operations: { omit: readonly [...Omit]; optional: [...Optional]; }, options?: ObjectOptions): TComposite<[TOmit<T, [...Omit, ...Optional]>, TPartial<TPick<T, Optional>>]>; /** * Create a schema for a string enum. * * @param values * @param options */ enum<T extends string[]>(values: [...T], options?: StringOptions): TUnsafe<T[number]>; /** * Create a schema for a datetime. * * @param options The options for the date. */ datetime(options?: StringOptions): TString$1; /** * Create a schema for a date. * * @param options */ date(options?: StringOptions): TString$1; /** * Create a schema for uuid. * * @param options The options for the duration. */ uuid(options?: StringOptions): TString$1; unsafe<T>(kind: string, options?: UnsafeOptions): TUnsafe<T>; file(options?: { max?: number; }): TFile; stream(): TStream; /** * Create a schema for a string enum e.g. LIKE_THIS. * * @param options */ snakeCase: (options?: StringOptions) => TString$1; /** * Create a schema for an object with a value and label. */ valueLabel: (options?: ObjectOptions) => TObject$1<{ value: TString$1; label: TString$1; description: TypeBox.TOptional<TString$1>; }>; } interface FileLike { /** * Filename. * @default "file" */ name: string; /** * Mandatory MIME type of the file. * @default "application/octet-stream" */ type: string; /** * Size of the file in bytes. * * Always 0 for streams, as the size is not known until the stream is fully read. * * @default 0 */ size: number; /** * Last modified timestamp in milliseconds since epoch. * * Always the current timestamp for streams, as the last modified time is not known. * We use this field to ensure compatibility with File API. * * @default Date.now() */ lastModified: number; /** * Returns a ReadableStream or Node.js Readable stream of the file content. * * For streams, this is the original stream. */ stream(): StreamLike; /** * Returns the file content as an ArrayBuffer. * * For streams, this reads the entire stream into memory. */ arrayBuffer(): Promise<ArrayBuffer>; /** * Returns the file content as a string. * * For streams, this reads the entire stream into memory and converts it to a string. */ text(): Promise<string>; /** * Optional file path, if the file is stored on disk. * * This is not from the File API, but rather a custom field to indicate where the file is stored. */ filepath?: string; } /** * TypeBox view of FileLike. */ type TFile = TUnsafe<FileLike>; declare const isTypeFile: (value: TSchema$1) => value is TFile; declare const isFileLike: (value: any) => value is FileLike; type StreamLike = ReadableStream | ReadableStream$1 | Readable | NodeJS.ReadableStream; type TStream = TUnsafe<StreamLike>; declare const isTypeStream: (value: TSchema$1) => value is TStream; type TextLength = "short" | "long" | "rich"; interface AlephaStringOptions extends StringOptions { size?: TextLength; } declare const t: TypeProvider; declare function isISODate(str: string): boolean; declare function isISODateTime(value: string): boolean; declare function isUUID(value: string): boolean; declare function isEmail(value: string): boolean; //# sourceMappingURL=TypeProvider.d.ts.map //#endregion //#region src/index.d.ts declare global { interface ImportMetaEnv { SSR: boolean; } interface ImportMeta { readonly env: ImportMetaEnv; } } /** * */ declare const run: (entry: Alepha | Service | Array<Service>, opts?: RunOptions) => void; //# sourceMappingURL=index.d.ts.map //#endregion export { $cursor, $env, $hook, $inject, $logger, $module, AbstractClass, Alepha, AlephaError, AlephaStringOptions, AlsProvider, AppNotStartedError, Async, AsyncFn, AsyncLocalStorageData, COLORS, CircularDependencyError, ContainerLockedError, CursorDescriptor, Descriptor, DescriptorArgs, DescriptorConfig, DescriptorFactory, DescriptorFactoryLike, Env, FileLike, Hook, HookDescriptor, HookOptions, Hooks, InjectDescriptor, InstantiableClass, KIND, LEVEL_COLORS, LogLevel, Logger, LoggerDescriptorOptions, LoggerEnv, LoggerOptions, MaybePromise, MockLogger, MockLoggerStore, Module, ModuleDescriptorOptions, OPTIONS, PRIMITIVE, Service, ServiceEntry, ServiceSubstitution, ServiceWithModule, State, type Static, type StaticDecode, type StaticEncode, StreamLike, type TAny, type TArray, type TBoolean, TFile, type TNumber, type TObject, type TOptional, type TProperties, type TRecord, type TSchema, TStream, type TString, TextLength, TypeBox, TypeBoxError, TypeBoxValue, TypeGuard, TypeProvider, __alephaRef, createDescriptor, isEmail, isFileLike, isISODate, isISODateTime, isModule, isTypeFile, isTypeStream, isUUID, run, t, toModuleName }; //# sourceMappingURL=index.d.ts.map