UNPKG

@h3ravel/core

Version:

Core application container, lifecycle management and service providers for H3ravel.

550 lines (539 loc) 16.3 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/Container.ts var Container; var init_Container = __esm({ "src/Container.ts"() { "use strict"; Container = class { static { __name(this, "Container"); } bindings = /* @__PURE__ */ new Map(); singletons = /* @__PURE__ */ new Map(); /** * Check if the target has any decorators * * @param target * @returns */ static hasAnyDecorator(target) { if (Reflect.getMetadataKeys(target).length > 0) return true; const paramLength = target.length; for (let i = 0; i < paramLength; i++) { if (Reflect.getMetadataKeys(target, `__param_${i}`).length > 0) { return true; } } return false; } bind(key, factory) { this.bindings.set(key, factory); } /** * Bind a singleton service to the container */ singleton(key, factory) { this.bindings.set(key, () => { if (!this.singletons.has(key)) { this.singletons.set(key, factory()); } return this.singletons.get(key); }); } /** * Resolve a service from the container */ make(key) { if (this.bindings.has(key)) { return this.bindings.get(key)(); } if (typeof key === "function") { return this.build(key); } throw new Error(`No binding found for key: ${typeof key === "string" ? key : key?.name}`); } /** * Automatically build a class with constructor dependency injection */ build(ClassType) { let dependencies = []; if (Array.isArray(ClassType.__inject__)) { dependencies = ClassType.__inject__.map((alias) => { return this.make(alias); }); } else { const paramTypes = Reflect.getMetadata("design:paramtypes", ClassType) || []; dependencies = paramTypes.map((dep) => this.make(dep)); } return new ClassType(...dependencies); } /** * Check if a service is registered */ has(key) { return this.bindings.has(key); } }; } }); // src/Application.ts var import_shared, import_dotenv, import_node_path, Application; var init_Application = __esm({ "src/Application.ts"() { "use strict"; init_Container(); import_shared = require("@h3ravel/shared"); import_dotenv = __toESM(require("dotenv"), 1); import_node_path = __toESM(require("path"), 1); Application = class _Application extends Container { static { __name(this, "Application"); } paths = new import_shared.PathLoader(); booted = false; versions = { app: "0", ts: "0" }; basePath; providers = []; externalProviders = []; constructor(basePath) { super(); this.basePath = basePath; this.setPath("base", basePath); this.loadOptions(); this.registerBaseBindings(); import_dotenv.default.config({ quiet: true }); } /** * Register core bindings into the container */ registerBaseBindings() { this.bind(_Application, () => this); this.bind("path.base", () => this.basePath); this.bind("load.paths", () => this.paths); } /** * Dynamically register all configured providers */ async registerConfiguredProviders() { const providers = await this.getAllProviders(); for (const ProviderClass of providers) { if (!ProviderClass) continue; const provider = new ProviderClass(this); await this.register(provider); } } async loadOptions() { const app = await this.safeImport(this.getPath("base", "package.json")); const core = await this.safeImport("../package.json"); if (app && app.dependencies) { this.versions.app = app.dependencies["@h3ravel/core"]; } if (core && core.devDependencies) { this.versions.ts = app.devDependencies.typescript; } } /** * Load default and optional providers dynamically * * Auto-Registration Behavior * * Minimal App: Loads only core, config, http, router by default. * Full-Stack App: Installs database, mail, queue, cache → they self-register via their providers. */ async getConfiguredProviders() { return [ (await Promise.resolve().then(() => (init_src(), src_exports))).CoreServiceProvider, (await Promise.resolve().then(() => (init_src(), src_exports))).ViewServiceProvider ]; } async getAllProviders() { const coreProviders = await this.getConfiguredProviders(); const allProviders = [ ...coreProviders, ...this.externalProviders ]; const uniqueProviders = Array.from(new Set(allProviders)); return this.sortProviders(uniqueProviders); } sortProviders(providers) { const priorityMap = /* @__PURE__ */ new Map(); providers.forEach((Provider) => { priorityMap.set(Provider.name, Provider.priority ?? 0); }); providers.forEach((Provider) => { const order = Provider.order; if (!order) return; const [direction, target] = order.split(":"); const targetPriority = priorityMap.get(target) ?? 0; if (direction === "before") { priorityMap.set(Provider.name, targetPriority - 1); } else if (direction === "after") { priorityMap.set(Provider.name, targetPriority + 1); } }); const sorted = providers.sort((A, B) => (priorityMap.get(B.name) ?? 0) - (priorityMap.get(A.name) ?? 0)); if (process.env.APP_DEBUG === "true") { console.table(sorted.map((P) => ({ Provider: P.name, Priority: priorityMap.get(P.name), Order: P.order || "N/A" }))); } return sorted; } registerProviders(providers) { this.externalProviders.push(...providers); } /** * Register a provider */ async register(provider) { await provider.register(); this.providers.push(provider); } /** * Boot all providers after registration */ async boot() { if (this.booted) return; for (const provider of this.providers) { if (provider.boot) { await provider.boot(); } } this.booted = true; } /** * Attempt to dynamically import an optional module */ async safeImport(moduleName) { try { const mod = await import(moduleName); return mod.default ?? mod ?? {}; } catch { return null; } } /** * Get the base path of the app * * @returns */ getBasePath() { return this.basePath; } /** * Dynamically retrieves a path property from the class. * Any property ending with "Path" is accessible automatically. * * @param name - The base name of the path property * @returns */ getPath(name, pth) { return import_node_path.default.join(this.paths.getPath(name, this.basePath), pth ?? ""); } /** * Programatically set the paths. * * @param name - The base name of the path property * @param path - The new path * @returns */ setPath(name, path2) { return this.paths.setPath(name, path2, this.basePath); } /** * Returns the installed version of the system core and typescript. * * @returns */ getVersion(key) { return this.versions[key]?.replaceAll(/\^|~/g, ""); } }; } }); // src/Controller.ts var Controller; var init_Controller = __esm({ "src/Controller.ts"() { "use strict"; Controller = class { static { __name(this, "Controller"); } app; constructor(app) { this.app = app; } show(..._ctx) { return; } index(..._ctx) { return; } store(..._ctx) { return; } update(..._ctx) { return; } destroy(..._ctx) { return; } }; } }); // src/ServiceProvider.ts var ServiceProvider; var init_ServiceProvider = __esm({ "src/ServiceProvider.ts"() { "use strict"; ServiceProvider = class { static { __name(this, "ServiceProvider"); } static order; static priority = 0; app; constructor(app) { this.app = app; } }; } }); // src/Contracts/ServiceProviderConstructor.ts var init_ServiceProviderConstructor = __esm({ "src/Contracts/ServiceProviderConstructor.ts"() { "use strict"; } }); // src/Di/Inject.ts function Inject(...dependencies) { return function(target) { target.__inject__ = dependencies; }; } function Injectable() { return (...args) => { if (args.length === 1) { void args[0]; } if (args.length === 3) { void args[0]; void args[1]; void args[2]; } }; } var init_Inject = __esm({ "src/Di/Inject.ts"() { "use strict"; __name(Inject, "Inject"); __name(Injectable, "Injectable"); } }); // src/Exceptions/Handler.ts var init_Handler = __esm({ "src/Exceptions/Handler.ts"() { "use strict"; } }); // src/Http/Kernel.ts var Kernel; var init_Kernel = __esm({ "src/Http/Kernel.ts"() { "use strict"; Kernel = class { static { __name(this, "Kernel"); } context; middleware; /** * @param context - A factory function that converts an H3Event into an HttpContext. * @param middleware - An array of middleware classes that will be executed in sequence. */ constructor(context, middleware = []) { this.context = context; this.middleware = middleware; } /** * Handles an incoming request and passes it through middleware before invoking the next handler. * * @param event - The raw H3 event object. * @param next - A callback function that represents the next layer (usually the controller or final handler). * @returns A promise resolving to the result of the request pipeline. */ async handle(event, next) { const ctx = this.context(event); const { app } = ctx.request; app.bind("view", () => async (template, params) => { const edge = app.make("edge"); return ctx.response.html(await edge.render(template, params)); }); const result = await this.runMiddleware(ctx, () => next(ctx)); if (result !== void 0 && this.isPlainObject(result)) { event.res.headers.set("Content-Type", "application/json; charset=UTF-8"); } return result; } /** * Sequentially runs middleware in the order they were registered. * * @param context - The standardized HttpContext. * @param next - Callback to execute when middleware completes. * @returns A promise resolving to the final handler's result. */ async runMiddleware(context, next) { let index = -1; const runner = /* @__PURE__ */ __name(async (i) => { if (i <= index) throw new Error("next() called multiple times"); index = i; const middleware = this.middleware[i]; if (middleware) { return middleware.handle(context, () => runner(i + 1)); } else { return next(context); } }, "runner"); return runner(0); } /** * Utility function to determine if a value is a plain object or array. * * @param value - The value to check. * @returns True if the value is a plain object or array, otherwise false. */ isPlainObject(value) { return typeof value === "object" && value !== null && (value.constructor === Object || value.constructor === Array); } }; } }); // src/Providers/CoreServiceProvider.ts var import_reflect_metadata, CoreServiceProvider; var init_CoreServiceProvider = __esm({ "src/Providers/CoreServiceProvider.ts"() { "use strict"; import_reflect_metadata = require("reflect-metadata"); init_ServiceProvider(); CoreServiceProvider = class extends ServiceProvider { static { __name(this, "CoreServiceProvider"); } static priority = 999; register() { } }; } }); // src/Providers/ViewServiceProvider.ts var import_edge, ViewServiceProvider; var init_ViewServiceProvider = __esm({ "src/Providers/ViewServiceProvider.ts"() { "use strict"; import_edge = require("edge.js"); init_ServiceProvider(); ViewServiceProvider = class extends ServiceProvider { static { __name(this, "ViewServiceProvider"); } static priority = 995; register() { const config = this.app.make("config"); const edge = import_edge.Edge.create({ cache: process.env.NODE_ENV === "production" }); edge.mount(this.app.getPath("views")); edge.global("asset", this.app.make("asset")); edge.global("config", config.get); edge.global("app", this.app); this.app.bind("edge", () => edge); } }; } }); // src/index.ts var src_exports = {}; __export(src_exports, { Application: () => Application, Container: () => Container, Controller: () => Controller, CoreServiceProvider: () => CoreServiceProvider, Inject: () => Inject, Injectable: () => Injectable, Kernel: () => Kernel, ServiceProvider: () => ServiceProvider, ViewServiceProvider: () => ViewServiceProvider }); module.exports = __toCommonJS(src_exports); var init_src = __esm({ "src/index.ts"() { init_Application(); init_Container(); init_Controller(); init_ServiceProvider(); init_ServiceProviderConstructor(); init_Inject(); init_Handler(); init_Kernel(); init_CoreServiceProvider(); init_ViewServiceProvider(); } }); init_src(); // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Application, Container, Controller, CoreServiceProvider, Inject, Injectable, Kernel, ServiceProvider, ViewServiceProvider }); //# sourceMappingURL=index.cjs.map