@solid-stack/agnos-express 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -0
- package/README.md +107 -0
- package/dist/constants/index.d.ts +8 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/core/ExpressPresentation.d.ts +50 -0
- package/dist/core/ExpressPresentation.d.ts.map +1 -0
- package/dist/core/ExpressRoute.d.ts +24 -0
- package/dist/core/ExpressRoute.d.ts.map +1 -0
- package/dist/core/ExpressRouter.d.ts +20 -0
- package/dist/core/ExpressRouter.d.ts.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/loadExpressPresentation.d.ts +16 -0
- package/dist/core/loadExpressPresentation.d.ts.map +1 -0
- package/dist/index.cjs +387 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +373 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/public.d.ts +76 -0
- package/dist/types/public.d.ts.map +1 -0
- package/dist/utils/autoLoadFeature.d.ts +15 -0
- package/dist/utils/autoLoadFeature.d.ts.map +1 -0
- package/dist/utils/autoLoadMainPx.d.ts +9 -0
- package/dist/utils/autoLoadMainPx.d.ts.map +1 -0
- package/dist/utils/folderExists.d.ts +8 -0
- package/dist/utils/folderExists.d.ts.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types/public.ts","../src/core/ExpressPresentation.ts","../src/utils/folderExists.ts","../src/utils/autoLoadMainPx.ts","../src/core/ExpressRouter.ts","../src/core/ExpressRoute.ts","../src/constants/index.ts","../src/utils/autoLoadFeature.ts","../src/core/loadExpressPresentation.ts"],"names":["ValueToken","MultiToken","MakeInjectable","ExpressPresentation","express","ILogger","Router","stat","fs","PresentationError","path","pathToFileURL","_init","FeatureLoadError","diFactoryRegistry","registerInjectable","IPresentation"],"mappings":"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","file":"index.cjs","sourcesContent":["import { ValueToken, MultiToken } from \"@solid-stack/di\";\nimport type { RequestHandler, ErrorRequestHandler } from \"express\";\nimport type { ExpressRoute } from \"../core/ExpressRoute.js\";\nimport type { ExpressRouter } from \"../core/ExpressRouter.js\";\n\n/**\n * Express Presentation configuration settings.\n */\nexport interface ExpressPxConfigs {\n /**\n * The port on which the Express HTTP server should listen.\n * @default 3000\n */\n port: number;\n\n /**\n * Optional host interface to bind the server to (e.g. '0.0.0.0' or 'localhost').\n */\n host?: string;\n}\n\n/**\n * Options for configuring and bootstrapping Express presentation via loadExpressPresentation.\n */\nexport interface ExpressPresentationOptions {\n /**\n * Port number for the Express server.\n * @default 3000\n */\n port?: number;\n\n /**\n * Optional host interface to bind the server to.\n */\n host?: string;\n\n /**\n * Absolute or relative path to the main presentation directory containing global middlewares and error handlers.\n */\n mainDir?: string;\n\n /**\n * Absolute or relative path to the features directory containing modular feature folders with Express route handlers.\n */\n featuresDir?: string;\n}\n\n/**\n * Express route HTTP methods supported.\n */\nexport type ExpressRouteMethod =\n | \"get\"\n | \"post\"\n | \"put\"\n | \"patch\"\n | \"delete\"\n | \"options\"\n | \"head\"\n | \"all\";\n\n/**\n * Interface contract for global Express middlewares.\n */\nexport interface ExpressMiddleware {\n handler: RequestHandler;\n}\n\n/**\n * Interface contract for global Express error handlers.\n */\nexport interface ExpressErrorHandler {\n handler: ErrorRequestHandler;\n}\n\n/**\n * DI ValueToken for injecting Express Presentation configuration.\n */\nexport class ExpressPxConfigsToken extends ValueToken<ExpressPxConfigs> {}\n\n/**\n * DI MultiToken for registering and resolving Express routers.\n */\nexport class ExpressRoutersToken extends MultiToken<ExpressRouter> {}\n\n/**\n * DI MultiToken for registering and resolving global Express middlewares.\n */\nexport class ExpressMiddlewaresToken extends MultiToken<ExpressMiddleware> {}\n\n/**\n * DI MultiToken for registering and resolving global Express error handlers.\n */\nexport class ExpressErrorHandlersToken extends MultiToken<ExpressErrorHandler> {}\n","import type { Server } from \"node:http\";\nimport express, {\n Router,\n type Express,\n type RequestHandler,\n type ErrorRequestHandler,\n} from \"express\";\nimport { MakeInjectable, type DepsType } from \"@solid-stack/di\";\nimport { IPresentation, ILogger } from \"@solid-stack/agnos\";\nimport {\n ExpressPxConfigsToken,\n ExpressRoutersToken,\n ExpressMiddlewaresToken,\n ExpressErrorHandlersToken,\n} from \"../types/index.js\";\n\n/**\n * Express HTTP Presentation Layer implementation for the Agnos framework.\n *\n * Implements the IPresentation lifecycle contract:\n * - init: Configures JSON body parsing, attaches global middlewares, routers, and error handlers.\n * - run: Starts the HTTP server on the configured port and host.\n * - stop: Gracefully shuts down the HTTP server.\n */\n@MakeInjectable\nexport class ExpressPresentation implements IPresentation {\n private readonly app: Express;\n private server: Server | null = null;\n\n public static deps = {\n configs: ExpressPxConfigsToken,\n errorHandlers: ExpressErrorHandlersToken,\n middlewares: ExpressMiddlewaresToken,\n routers: ExpressRoutersToken,\n logger: ILogger,\n };\n\n constructor(public deps: DepsType<typeof ExpressPresentation.deps>) {\n this.app = express();\n }\n\n /**\n * Initializes middleware, feature routers, and error handlers on the Express app.\n */\n async init(): Promise<void> {\n this.app.use(express.json());\n\n this.attachMiddlewares();\n this.attachRouters();\n this.attachErrorHandlers();\n }\n\n private attachMiddlewares(): void {\n const middlewares = this.deps.middlewares ?? [];\n for (const middleware of middlewares) {\n this.app.use((req, res, next) => middleware.handler(req, res, next));\n }\n }\n\n private attachRouters(): void {\n const routers = this.deps.routers ?? [];\n this.deps.logger.debug(\"Registering Express routers...\", {\n count: routers.length,\n });\n\n for (const subRouter of routers) {\n const router = Router();\n\n // Attach router-level middlewares if present\n if (subRouter.middlewares && subRouter.middlewares.length > 0) {\n for (const mw of subRouter.middlewares) {\n router.use(mw);\n }\n }\n\n // Attach individual routes\n for (const route of subRouter.routes) {\n this.deps.logger.debug(\n `Registering route: [${route.method.toUpperCase()}] ${subRouter.path}${route.path}`,\n );\n\n const routeHandler: RequestHandler = (req, res, next) =>\n route.handler(req, res, next);\n\n if (route.middlewares && route.middlewares.length > 0) {\n router[route.method](route.path, ...route.middlewares, routeHandler);\n } else {\n router[route.method](route.path, routeHandler);\n }\n }\n\n this.app.use(subRouter.path, router);\n }\n }\n\n private attachErrorHandlers(): void {\n const errorHandlers = this.deps.errorHandlers ?? [];\n for (const errorHandler of errorHandlers) {\n const handler: ErrorRequestHandler = (err, req, res, next) =>\n errorHandler.handler(err, req, res, next);\n this.app.use(handler);\n }\n }\n\n /**\n * Starts the Express server listening on the configured port and host.\n */\n async run(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n const { port, host } = this.deps.configs;\n\n const onListening = () => {\n this.deps.logger.info(\n `Express server listening on ${host ? `${host}:` : \"port \"}${port}`,\n );\n resolve();\n };\n\n if (host) {\n this.server = this.app.listen(port, host, onListening);\n } else {\n this.server = this.app.listen(port, onListening);\n }\n\n this.server.on(\"error\", (err: Error) => {\n this.deps.logger.error(\"Express server error:\", err);\n reject(err);\n });\n } catch (err) {\n reject(err);\n }\n });\n }\n\n /**\n * Gracefully closes the running HTTP server.\n */\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.server) {\n return resolve();\n }\n\n this.server.close((err) => {\n if (err) {\n this.deps.logger.error(\"Error closing Express server:\", err);\n return reject(err);\n }\n this.deps.logger.info(\"Express server stopped receiving new requests.\");\n this.server = null;\n resolve();\n });\n });\n }\n\n /**\n * Returns the underlying Express application instance.\n */\n public getApp(): Express {\n return this.app;\n }\n\n /**\n * Returns the active Node HTTP server instance, or null if not currently running.\n */\n public getServer(): Server | null {\n return this.server;\n }\n}\n","import { stat } from \"node:fs/promises\";\n\n/**\n * Checks if a given filesystem path exists and is a directory.\n *\n * @param dir - The directory path to check\n * @returns True if the directory exists, false if it does not exist\n */\nexport async function folderExists(dir: string): Promise<boolean> {\n try {\n const stats = await stat(dir);\n return stats.isDirectory();\n } catch (error: any) {\n if (error?.code === \"ENOENT\") {\n return false;\n }\n throw error;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type { Container } from \"@solid-stack/di\";\nimport { PresentationError } from \"@solid-stack/agnos\";\nimport {\n ExpressMiddlewaresToken,\n ExpressErrorHandlersToken,\n} from \"../types/index.js\";\nimport { folderExists } from \"./folderExists.js\";\n\n/**\n * Loads global middlewares and error handlers from the main presentation directory.\n *\n * @param c - DI Container\n * @param mainDir - Directory containing main presentation index file\n */\nexport async function autoLoadMainPx(\n c: Container,\n mainDir: string,\n): Promise<void> {\n if (!(await folderExists(mainDir))) {\n return;\n }\n\n const mainDirContents = await fs.readdir(mainDir, { withFileTypes: true });\n\n const indexFile = mainDirContents.find(\n (f) =>\n f.isFile() &&\n (f.name === \"index.ts\" ||\n f.name === \"index.js\" ||\n f.name === \"index.mjs\" ||\n f.name === \"index.cjs\"),\n );\n\n if (!indexFile) {\n throw new PresentationError(\n `No index.ts or index.js file found in the main presentation directory: ${mainDir}`,\n { phase: \"init\" },\n );\n }\n\n const indexFilePath = path.join(mainDir, indexFile.name);\n const imported = await import(pathToFileURL(indexFilePath).href);\n\n // Register middlewares if exported\n const middlewares = imported.middlewares;\n if (middlewares && Array.isArray(middlewares)) {\n for (const middleware of middlewares) {\n c.provideMulti(ExpressMiddlewaresToken, middleware);\n }\n }\n\n // Register error handlers if exported\n const errorHandlers = imported.errorHandlers;\n if (errorHandlers && Array.isArray(errorHandlers)) {\n for (const errorHandler of errorHandlers) {\n c.provideMulti(ExpressErrorHandlersToken, errorHandler);\n }\n }\n}\n","import type { RequestHandler } from \"express\";\nimport type { ExpressRoute } from \"./ExpressRoute.js\";\n\n/**\n * Abstract base class for defining an Express router grouping multiple routes under a common path prefix.\n */\nexport abstract class ExpressRouter {\n /**\n * The base URL path prefix for this router (e.g. '/users' or '/api/v1/orders').\n */\n public abstract get path(): string;\n\n /**\n * The collection of routes registered under this router.\n */\n public abstract get routes(): ExpressRoute[];\n\n /**\n * Optional router-level middlewares executed for all routes in this router.\n */\n public middlewares?: RequestHandler[];\n}\n","import type { RequestHandler } from \"express\";\nimport type { ExpressRouteMethod } from \"../types/index.js\";\n\n/**\n * Abstract base class for defining an Express route handler within an Agnos feature.\n */\nexport abstract class ExpressRoute {\n /**\n * The HTTP method for this route (e.g. 'get', 'post', 'put', 'delete').\n */\n abstract method: ExpressRouteMethod;\n\n /**\n * The sub-path for this route relative to the feature router's base path (e.g. '/' or '/:id').\n */\n abstract path: string;\n\n /**\n * The Express request handler function.\n */\n abstract handler: RequestHandler;\n\n /**\n * Optional route-level middlewares executed before handler.\n */\n middlewares?: RequestHandler[];\n}\n","/**\n * Default configuration constants for Agnos Express presentation.\n */\n\nexport const DEFAULT_PORT = 3000;\nexport const DEFAULT_HOST = \"0.0.0.0\";\nexport const DEFAULT_FEATURE_PX_DIR = \"pxExpress\";\nexport const DEFAULT_HANDLERS_DIR = \"handlers\";\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport {\n MakeInjectable,\n registerInjectable,\n diFactoryRegistry,\n type Container,\n type DepsType,\n} from \"@solid-stack/di\";\nimport { FeatureLoadError } from \"@solid-stack/agnos\";\nimport { ExpressRoutersToken } from \"../types/index.js\";\nimport { ExpressRouter } from \"../core/ExpressRouter.js\";\nimport { ExpressRoute } from \"../core/ExpressRoute.js\";\nimport { folderExists } from \"./folderExists.js\";\nimport {\n DEFAULT_FEATURE_PX_DIR,\n DEFAULT_HANDLERS_DIR,\n} from \"../constants/index.js\";\n\n/**\n * Autoloads Express presentation modules from feature directories.\n *\n * For each feature directory:\n * 1. Checks for 'pxExpress' folder.\n * 2. Checks for index.ts/js exporting 'path' (string prefix).\n * 3. Scans 'handlers' folder for ExpressRoute implementations.\n * 4. Dynamically builds a FeatureRouter class registered with ExpressRoutersToken.\n *\n * @param c - DI Container\n * @param featuresDir - Path to features directory\n */\nexport async function autoLoadFeature(\n c: Container,\n featuresDir: string,\n): Promise<void> {\n if (!(await folderExists(featuresDir))) {\n return;\n }\n\n const features = await fs.readdir(featuresDir, { withFileTypes: true });\n\n for (const feature of features) {\n if (!feature.isDirectory()) continue;\n\n const featureName = feature.name;\n const pxExpressDir = path.join(\n featuresDir,\n featureName,\n DEFAULT_FEATURE_PX_DIR,\n );\n\n if (!(await folderExists(pxExpressDir))) continue;\n\n const pxExpressDirFiles = await fs.readdir(pxExpressDir);\n const indexFile = pxExpressDirFiles.find(\n (f) =>\n f === \"index.ts\" ||\n f === \"index.js\" ||\n f === \"index.mjs\" ||\n f === \"index.cjs\",\n );\n\n if (!indexFile) {\n throw new FeatureLoadError(\n featureName,\n pxExpressDir,\n `The express presentation of feature \"${featureName}\" (${pxExpressDir}) does not have an index.ts or index.js file.`,\n );\n }\n\n const indexFilePath = path.join(pxExpressDir, indexFile);\n const imported = await import(pathToFileURL(indexFilePath).href);\n\n const routePath = imported.path;\n\n if (typeof routePath !== \"string\") {\n throw new FeatureLoadError(\n featureName,\n indexFilePath,\n `The express presentation of feature \"${featureName}\" (${pxExpressDir}) has an index file, but it doesn't export a string named 'path'.`,\n );\n }\n\n // Load routes from handlers/\n const routeClasses: {\n meta: { name: string };\n class: new (...args: any[]) => ExpressRoute;\n }[] = [];\n\n const routesDir = path.join(pxExpressDir, DEFAULT_HANDLERS_DIR);\n\n if (await folderExists(routesDir)) {\n const routeFiles = await fs.readdir(routesDir);\n\n for (const routeFile of routeFiles) {\n if (\n !routeFile.match(/\\.(js|ts|mjs|cjs)$/) ||\n routeFile.endsWith(\".d.ts\") ||\n routeFile.includes(\".test.\") ||\n routeFile.includes(\".spec.\")\n ) {\n continue;\n }\n\n const routeFilePath = path.join(routesDir, routeFile);\n const routeFileModule = await import(pathToFileURL(routeFilePath).href);\n const routeClass = routeFileModule.default;\n\n if (!routeClass) {\n throw new FeatureLoadError(\n featureName,\n routeFilePath,\n `The route file \"${routeFile}\" in feature \"${featureName}\" doesn't have a default export.`,\n );\n }\n\n if (\n typeof routeClass !== \"function\" ||\n !(routeClass.prototype instanceof ExpressRoute)\n ) {\n throw new FeatureLoadError(\n featureName,\n routeFilePath,\n `The default export in \"${routeFile}\" (feature: \"${featureName}\") does not inherit from ExpressRoute.`,\n );\n }\n\n // Ensure DI factory is registered for the route class\n if (!diFactoryRegistry.has(routeClass)) {\n if ((routeClass as any).deps) {\n registerInjectable(routeClass as any);\n } else {\n diFactoryRegistry.set(\n routeClass,\n () => new (routeClass as any)(),\n );\n }\n }\n\n routeClasses.push({\n meta: { name: routeClass.name },\n class: routeClass,\n });\n }\n }\n\n // Build the dependency object for DI resolution\n const featureDeps = routeClasses.reduce(\n (acc, entry) => {\n const depKey =\n entry.meta.name.charAt(0).toLowerCase() + entry.meta.name.slice(1);\n acc[depKey] = entry.class;\n return acc;\n },\n {} as Record<string, new (...args: any[]) => ExpressRoute>,\n );\n\n const optionalMiddlewares = Array.isArray(imported.middlewares)\n ? imported.middlewares\n : undefined;\n\n // Create the router class dynamically\n @MakeInjectable\n class FeatureRouter extends ExpressRouter {\n public static deps = featureDeps;\n public middlewares = optionalMiddlewares;\n\n constructor(public deps: DepsType<typeof featureDeps>) {\n super();\n }\n\n public get path(): string {\n return routePath;\n }\n\n public get routes(): ExpressRoute[] {\n return Object.values(this.deps) as ExpressRoute[];\n }\n }\n\n const routerClassName = `${\n featureName.charAt(0).toUpperCase() + featureName.slice(1)\n }ExpressRouter`;\n\n Object.defineProperty(FeatureRouter, \"name\", {\n value: routerClassName,\n });\n\n c.provideMulti(ExpressRoutersToken, FeatureRouter);\n }\n}\n","import type { Container } from \"@solid-stack/di\";\nimport { IPresentation } from \"@solid-stack/agnos\";\nimport { ExpressPresentation } from \"./ExpressPresentation.js\";\nimport {\n ExpressPxConfigsToken,\n type ExpressPresentationOptions,\n} from \"../types/index.js\";\nimport { autoLoadMainPx } from \"../utils/autoLoadMainPx.js\";\nimport { autoLoadFeature } from \"../utils/autoLoadFeature.js\";\nimport { DEFAULT_PORT } from \"../constants/index.js\";\n\n/**\n * Bootstraps and registers the Express presentation layer in the DI container.\n *\n * Configures:\n * 1. ExpressPxConfigsToken with port and optional host.\n * 2. Autoloads global middlewares and error handlers from mainDir if specified.\n * 3. Autoloads feature routes from featuresDir if specified.\n * 4. Binds IPresentation to ExpressPresentation.\n *\n * @param container - The @solid-stack/di Container instance.\n * @param options - Presentation bootstrap options.\n */\nexport const loadExpressPresentation = async (\n container: Container,\n options?: ExpressPresentationOptions,\n): Promise<void> => {\n const port = options?.port ?? DEFAULT_PORT;\n const host = options?.host;\n\n container.provideValue(ExpressPxConfigsToken, {\n port,\n ...(host ? { host } : {}),\n });\n\n if (options?.mainDir) {\n await autoLoadMainPx(container, options.mainDir);\n }\n\n if (options?.featuresDir) {\n await autoLoadFeature(container, options.featuresDir);\n }\n\n container.provide(IPresentation, ExpressPresentation);\n};\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @solid-stack/agnos-express
|
|
3
|
+
*
|
|
4
|
+
* Express presentation adapter for the Agnos application architecture framework.
|
|
5
|
+
*/
|
|
6
|
+
export { ExpressPresentation, loadExpressPresentation, ExpressRoute, ExpressRouter, } from "./core/index.js";
|
|
7
|
+
export { ExpressPxConfigsToken, ExpressRoutersToken, ExpressMiddlewaresToken, ExpressErrorHandlersToken, type ExpressPxConfigs, type ExpressPresentationOptions, type ExpressMiddleware, type ExpressErrorHandler, type ExpressRouteMethod, } from "./types/index.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,YAAY,EACZ,aAAa,GACd,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,uBAAuB,EACvB,yBAAyB,EACzB,KAAK,gBAAgB,EACrB,KAAK,0BAA0B,EAC/B,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import express, { Router } from 'express';
|
|
2
|
+
import { ValueToken, MultiToken, diFactoryRegistry, registerInjectable, MakeInjectable } from '@solid-stack/di';
|
|
3
|
+
import { ILogger, IPresentation, PresentationError, FeatureLoadError } from '@solid-stack/agnos';
|
|
4
|
+
import fs2, { stat } from 'fs/promises';
|
|
5
|
+
import path2 from 'path';
|
|
6
|
+
import { pathToFileURL } from 'url';
|
|
7
|
+
|
|
8
|
+
var __create = Object.create;
|
|
9
|
+
var __defProp = Object.defineProperty;
|
|
10
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
11
|
+
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
12
|
+
var __typeError = (msg) => {
|
|
13
|
+
throw TypeError(msg);
|
|
14
|
+
};
|
|
15
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
16
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
17
|
+
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
|
|
18
|
+
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
19
|
+
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
20
|
+
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
|
|
21
|
+
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
22
|
+
var __runInitializers = (array, flags, self, value) => {
|
|
23
|
+
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) fns[i].call(self) ;
|
|
24
|
+
return value;
|
|
25
|
+
};
|
|
26
|
+
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
27
|
+
var it, done, ctx, k = flags & 7, p = false;
|
|
28
|
+
var j = 0;
|
|
29
|
+
var extraInitializers = array[j] || (array[j] = []);
|
|
30
|
+
var desc = k && ((target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(target , name));
|
|
31
|
+
__name(target, name);
|
|
32
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
33
|
+
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
34
|
+
it = (0, decorators[i])(target, ctx), done._ = 1;
|
|
35
|
+
__expectFn(it) && (target = it);
|
|
36
|
+
}
|
|
37
|
+
return __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
38
|
+
};
|
|
39
|
+
var ExpressPxConfigsToken = class extends ValueToken {
|
|
40
|
+
};
|
|
41
|
+
var ExpressRoutersToken = class extends MultiToken {
|
|
42
|
+
};
|
|
43
|
+
var ExpressMiddlewaresToken = class extends MultiToken {
|
|
44
|
+
};
|
|
45
|
+
var ExpressErrorHandlersToken = class extends MultiToken {
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// src/core/ExpressPresentation.ts
|
|
49
|
+
var _ExpressPresentation_decorators, _init;
|
|
50
|
+
_ExpressPresentation_decorators = [MakeInjectable];
|
|
51
|
+
var ExpressPresentation = class {
|
|
52
|
+
constructor(deps) {
|
|
53
|
+
this.deps = deps;
|
|
54
|
+
this.app = express();
|
|
55
|
+
}
|
|
56
|
+
deps;
|
|
57
|
+
app;
|
|
58
|
+
server = null;
|
|
59
|
+
static deps = {
|
|
60
|
+
configs: ExpressPxConfigsToken,
|
|
61
|
+
errorHandlers: ExpressErrorHandlersToken,
|
|
62
|
+
middlewares: ExpressMiddlewaresToken,
|
|
63
|
+
routers: ExpressRoutersToken,
|
|
64
|
+
logger: ILogger
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Initializes middleware, feature routers, and error handlers on the Express app.
|
|
68
|
+
*/
|
|
69
|
+
async init() {
|
|
70
|
+
this.app.use(express.json());
|
|
71
|
+
this.attachMiddlewares();
|
|
72
|
+
this.attachRouters();
|
|
73
|
+
this.attachErrorHandlers();
|
|
74
|
+
}
|
|
75
|
+
attachMiddlewares() {
|
|
76
|
+
const middlewares = this.deps.middlewares ?? [];
|
|
77
|
+
for (const middleware of middlewares) {
|
|
78
|
+
this.app.use((req, res, next) => middleware.handler(req, res, next));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
attachRouters() {
|
|
82
|
+
const routers = this.deps.routers ?? [];
|
|
83
|
+
this.deps.logger.debug("Registering Express routers...", {
|
|
84
|
+
count: routers.length
|
|
85
|
+
});
|
|
86
|
+
for (const subRouter of routers) {
|
|
87
|
+
const router = Router();
|
|
88
|
+
if (subRouter.middlewares && subRouter.middlewares.length > 0) {
|
|
89
|
+
for (const mw of subRouter.middlewares) {
|
|
90
|
+
router.use(mw);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
for (const route of subRouter.routes) {
|
|
94
|
+
this.deps.logger.debug(
|
|
95
|
+
`Registering route: [${route.method.toUpperCase()}] ${subRouter.path}${route.path}`
|
|
96
|
+
);
|
|
97
|
+
const routeHandler = (req, res, next) => route.handler(req, res, next);
|
|
98
|
+
if (route.middlewares && route.middlewares.length > 0) {
|
|
99
|
+
router[route.method](route.path, ...route.middlewares, routeHandler);
|
|
100
|
+
} else {
|
|
101
|
+
router[route.method](route.path, routeHandler);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
this.app.use(subRouter.path, router);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
attachErrorHandlers() {
|
|
108
|
+
const errorHandlers = this.deps.errorHandlers ?? [];
|
|
109
|
+
for (const errorHandler of errorHandlers) {
|
|
110
|
+
const handler = (err, req, res, next) => errorHandler.handler(err, req, res, next);
|
|
111
|
+
this.app.use(handler);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Starts the Express server listening on the configured port and host.
|
|
116
|
+
*/
|
|
117
|
+
async run() {
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
try {
|
|
120
|
+
const { port, host } = this.deps.configs;
|
|
121
|
+
const onListening = () => {
|
|
122
|
+
this.deps.logger.info(
|
|
123
|
+
`Express server listening on ${host ? `${host}:` : "port "}${port}`
|
|
124
|
+
);
|
|
125
|
+
resolve();
|
|
126
|
+
};
|
|
127
|
+
if (host) {
|
|
128
|
+
this.server = this.app.listen(port, host, onListening);
|
|
129
|
+
} else {
|
|
130
|
+
this.server = this.app.listen(port, onListening);
|
|
131
|
+
}
|
|
132
|
+
this.server.on("error", (err) => {
|
|
133
|
+
this.deps.logger.error("Express server error:", err);
|
|
134
|
+
reject(err);
|
|
135
|
+
});
|
|
136
|
+
} catch (err) {
|
|
137
|
+
reject(err);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Gracefully closes the running HTTP server.
|
|
143
|
+
*/
|
|
144
|
+
async stop() {
|
|
145
|
+
return new Promise((resolve, reject) => {
|
|
146
|
+
if (!this.server) {
|
|
147
|
+
return resolve();
|
|
148
|
+
}
|
|
149
|
+
this.server.close((err) => {
|
|
150
|
+
if (err) {
|
|
151
|
+
this.deps.logger.error("Error closing Express server:", err);
|
|
152
|
+
return reject(err);
|
|
153
|
+
}
|
|
154
|
+
this.deps.logger.info("Express server stopped receiving new requests.");
|
|
155
|
+
this.server = null;
|
|
156
|
+
resolve();
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Returns the underlying Express application instance.
|
|
162
|
+
*/
|
|
163
|
+
getApp() {
|
|
164
|
+
return this.app;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Returns the active Node HTTP server instance, or null if not currently running.
|
|
168
|
+
*/
|
|
169
|
+
getServer() {
|
|
170
|
+
return this.server;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
_init = __decoratorStart(null);
|
|
174
|
+
ExpressPresentation = __decorateElement(_init, 0, "ExpressPresentation", _ExpressPresentation_decorators, ExpressPresentation);
|
|
175
|
+
__runInitializers(_init, 1, ExpressPresentation);
|
|
176
|
+
async function folderExists(dir) {
|
|
177
|
+
try {
|
|
178
|
+
const stats = await stat(dir);
|
|
179
|
+
return stats.isDirectory();
|
|
180
|
+
} catch (error) {
|
|
181
|
+
if (error?.code === "ENOENT") {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/utils/autoLoadMainPx.ts
|
|
189
|
+
async function autoLoadMainPx(c, mainDir) {
|
|
190
|
+
if (!await folderExists(mainDir)) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const mainDirContents = await fs2.readdir(mainDir, { withFileTypes: true });
|
|
194
|
+
const indexFile = mainDirContents.find(
|
|
195
|
+
(f) => f.isFile() && (f.name === "index.ts" || f.name === "index.js" || f.name === "index.mjs" || f.name === "index.cjs")
|
|
196
|
+
);
|
|
197
|
+
if (!indexFile) {
|
|
198
|
+
throw new PresentationError(
|
|
199
|
+
`No index.ts or index.js file found in the main presentation directory: ${mainDir}`,
|
|
200
|
+
{ phase: "init" }
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
const indexFilePath = path2.join(mainDir, indexFile.name);
|
|
204
|
+
const imported = await import(pathToFileURL(indexFilePath).href);
|
|
205
|
+
const middlewares = imported.middlewares;
|
|
206
|
+
if (middlewares && Array.isArray(middlewares)) {
|
|
207
|
+
for (const middleware of middlewares) {
|
|
208
|
+
c.provideMulti(ExpressMiddlewaresToken, middleware);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const errorHandlers = imported.errorHandlers;
|
|
212
|
+
if (errorHandlers && Array.isArray(errorHandlers)) {
|
|
213
|
+
for (const errorHandler of errorHandlers) {
|
|
214
|
+
c.provideMulti(ExpressErrorHandlersToken, errorHandler);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/core/ExpressRouter.ts
|
|
220
|
+
var ExpressRouter = class {
|
|
221
|
+
/**
|
|
222
|
+
* Optional router-level middlewares executed for all routes in this router.
|
|
223
|
+
*/
|
|
224
|
+
middlewares;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// src/core/ExpressRoute.ts
|
|
228
|
+
var ExpressRoute = class {
|
|
229
|
+
/**
|
|
230
|
+
* Optional route-level middlewares executed before handler.
|
|
231
|
+
*/
|
|
232
|
+
middlewares;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// src/constants/index.ts
|
|
236
|
+
var DEFAULT_PORT = 3e3;
|
|
237
|
+
var DEFAULT_FEATURE_PX_DIR = "pxExpress";
|
|
238
|
+
var DEFAULT_HANDLERS_DIR = "handlers";
|
|
239
|
+
|
|
240
|
+
// src/utils/autoLoadFeature.ts
|
|
241
|
+
async function autoLoadFeature(c, featuresDir) {
|
|
242
|
+
var _FeatureRouter_decorators, _init2, _a;
|
|
243
|
+
if (!await folderExists(featuresDir)) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const features = await fs2.readdir(featuresDir, { withFileTypes: true });
|
|
247
|
+
for (const feature of features) {
|
|
248
|
+
if (!feature.isDirectory()) continue;
|
|
249
|
+
const featureName = feature.name;
|
|
250
|
+
const pxExpressDir = path2.join(
|
|
251
|
+
featuresDir,
|
|
252
|
+
featureName,
|
|
253
|
+
DEFAULT_FEATURE_PX_DIR
|
|
254
|
+
);
|
|
255
|
+
if (!await folderExists(pxExpressDir)) continue;
|
|
256
|
+
const pxExpressDirFiles = await fs2.readdir(pxExpressDir);
|
|
257
|
+
const indexFile = pxExpressDirFiles.find(
|
|
258
|
+
(f) => f === "index.ts" || f === "index.js" || f === "index.mjs" || f === "index.cjs"
|
|
259
|
+
);
|
|
260
|
+
if (!indexFile) {
|
|
261
|
+
throw new FeatureLoadError(
|
|
262
|
+
featureName,
|
|
263
|
+
pxExpressDir,
|
|
264
|
+
`The express presentation of feature "${featureName}" (${pxExpressDir}) does not have an index.ts or index.js file.`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
const indexFilePath = path2.join(pxExpressDir, indexFile);
|
|
268
|
+
const imported = await import(pathToFileURL(indexFilePath).href);
|
|
269
|
+
const routePath = imported.path;
|
|
270
|
+
if (typeof routePath !== "string") {
|
|
271
|
+
throw new FeatureLoadError(
|
|
272
|
+
featureName,
|
|
273
|
+
indexFilePath,
|
|
274
|
+
`The express presentation of feature "${featureName}" (${pxExpressDir}) has an index file, but it doesn't export a string named 'path'.`
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
const routeClasses = [];
|
|
278
|
+
const routesDir = path2.join(pxExpressDir, DEFAULT_HANDLERS_DIR);
|
|
279
|
+
if (await folderExists(routesDir)) {
|
|
280
|
+
const routeFiles = await fs2.readdir(routesDir);
|
|
281
|
+
for (const routeFile of routeFiles) {
|
|
282
|
+
if (!routeFile.match(/\.(js|ts|mjs|cjs)$/) || routeFile.endsWith(".d.ts") || routeFile.includes(".test.") || routeFile.includes(".spec.")) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
const routeFilePath = path2.join(routesDir, routeFile);
|
|
286
|
+
const routeFileModule = await import(pathToFileURL(routeFilePath).href);
|
|
287
|
+
const routeClass = routeFileModule.default;
|
|
288
|
+
if (!routeClass) {
|
|
289
|
+
throw new FeatureLoadError(
|
|
290
|
+
featureName,
|
|
291
|
+
routeFilePath,
|
|
292
|
+
`The route file "${routeFile}" in feature "${featureName}" doesn't have a default export.`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
if (typeof routeClass !== "function" || !(routeClass.prototype instanceof ExpressRoute)) {
|
|
296
|
+
throw new FeatureLoadError(
|
|
297
|
+
featureName,
|
|
298
|
+
routeFilePath,
|
|
299
|
+
`The default export in "${routeFile}" (feature: "${featureName}") does not inherit from ExpressRoute.`
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
if (!diFactoryRegistry.has(routeClass)) {
|
|
303
|
+
if (routeClass.deps) {
|
|
304
|
+
registerInjectable(routeClass);
|
|
305
|
+
} else {
|
|
306
|
+
diFactoryRegistry.set(
|
|
307
|
+
routeClass,
|
|
308
|
+
() => new routeClass()
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
routeClasses.push({
|
|
313
|
+
meta: { name: routeClass.name },
|
|
314
|
+
class: routeClass
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const featureDeps = routeClasses.reduce(
|
|
319
|
+
(acc, entry) => {
|
|
320
|
+
const depKey = entry.meta.name.charAt(0).toLowerCase() + entry.meta.name.slice(1);
|
|
321
|
+
acc[depKey] = entry.class;
|
|
322
|
+
return acc;
|
|
323
|
+
},
|
|
324
|
+
{}
|
|
325
|
+
);
|
|
326
|
+
const optionalMiddlewares = Array.isArray(imported.middlewares) ? imported.middlewares : void 0;
|
|
327
|
+
_FeatureRouter_decorators = [MakeInjectable];
|
|
328
|
+
class FeatureRouter extends (_a = ExpressRouter) {
|
|
329
|
+
constructor(deps) {
|
|
330
|
+
super();
|
|
331
|
+
this.deps = deps;
|
|
332
|
+
}
|
|
333
|
+
deps;
|
|
334
|
+
static deps = featureDeps;
|
|
335
|
+
middlewares = optionalMiddlewares;
|
|
336
|
+
get path() {
|
|
337
|
+
return routePath;
|
|
338
|
+
}
|
|
339
|
+
get routes() {
|
|
340
|
+
return Object.values(this.deps);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
_init2 = __decoratorStart(_a);
|
|
344
|
+
FeatureRouter = __decorateElement(_init2, 0, "FeatureRouter", _FeatureRouter_decorators, FeatureRouter);
|
|
345
|
+
__runInitializers(_init2, 1, FeatureRouter);
|
|
346
|
+
const routerClassName = `${featureName.charAt(0).toUpperCase() + featureName.slice(1)}ExpressRouter`;
|
|
347
|
+
Object.defineProperty(FeatureRouter, "name", {
|
|
348
|
+
value: routerClassName
|
|
349
|
+
});
|
|
350
|
+
c.provideMulti(ExpressRoutersToken, FeatureRouter);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/core/loadExpressPresentation.ts
|
|
355
|
+
var loadExpressPresentation = async (container, options) => {
|
|
356
|
+
const port = options?.port ?? DEFAULT_PORT;
|
|
357
|
+
const host = options?.host;
|
|
358
|
+
container.provideValue(ExpressPxConfigsToken, {
|
|
359
|
+
port,
|
|
360
|
+
...host ? { host } : {}
|
|
361
|
+
});
|
|
362
|
+
if (options?.mainDir) {
|
|
363
|
+
await autoLoadMainPx(container, options.mainDir);
|
|
364
|
+
}
|
|
365
|
+
if (options?.featuresDir) {
|
|
366
|
+
await autoLoadFeature(container, options.featuresDir);
|
|
367
|
+
}
|
|
368
|
+
container.provide(IPresentation, ExpressPresentation);
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
export { ExpressErrorHandlersToken, ExpressMiddlewaresToken, ExpressPresentation, ExpressPxConfigsToken, ExpressRoute, ExpressRouter, ExpressRoutersToken, loadExpressPresentation };
|
|
372
|
+
//# sourceMappingURL=index.js.map
|
|
373
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types/public.ts","../src/core/ExpressPresentation.ts","../src/utils/folderExists.ts","../src/utils/autoLoadMainPx.ts","../src/core/ExpressRouter.ts","../src/core/ExpressRoute.ts","../src/constants/index.ts","../src/utils/autoLoadFeature.ts","../src/core/loadExpressPresentation.ts"],"names":["fs","path","_init","pathToFileURL","MakeInjectable","IPresentation"],"mappings":"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","file":"index.js","sourcesContent":["import { ValueToken, MultiToken } from \"@solid-stack/di\";\nimport type { RequestHandler, ErrorRequestHandler } from \"express\";\nimport type { ExpressRoute } from \"../core/ExpressRoute.js\";\nimport type { ExpressRouter } from \"../core/ExpressRouter.js\";\n\n/**\n * Express Presentation configuration settings.\n */\nexport interface ExpressPxConfigs {\n /**\n * The port on which the Express HTTP server should listen.\n * @default 3000\n */\n port: number;\n\n /**\n * Optional host interface to bind the server to (e.g. '0.0.0.0' or 'localhost').\n */\n host?: string;\n}\n\n/**\n * Options for configuring and bootstrapping Express presentation via loadExpressPresentation.\n */\nexport interface ExpressPresentationOptions {\n /**\n * Port number for the Express server.\n * @default 3000\n */\n port?: number;\n\n /**\n * Optional host interface to bind the server to.\n */\n host?: string;\n\n /**\n * Absolute or relative path to the main presentation directory containing global middlewares and error handlers.\n */\n mainDir?: string;\n\n /**\n * Absolute or relative path to the features directory containing modular feature folders with Express route handlers.\n */\n featuresDir?: string;\n}\n\n/**\n * Express route HTTP methods supported.\n */\nexport type ExpressRouteMethod =\n | \"get\"\n | \"post\"\n | \"put\"\n | \"patch\"\n | \"delete\"\n | \"options\"\n | \"head\"\n | \"all\";\n\n/**\n * Interface contract for global Express middlewares.\n */\nexport interface ExpressMiddleware {\n handler: RequestHandler;\n}\n\n/**\n * Interface contract for global Express error handlers.\n */\nexport interface ExpressErrorHandler {\n handler: ErrorRequestHandler;\n}\n\n/**\n * DI ValueToken for injecting Express Presentation configuration.\n */\nexport class ExpressPxConfigsToken extends ValueToken<ExpressPxConfigs> {}\n\n/**\n * DI MultiToken for registering and resolving Express routers.\n */\nexport class ExpressRoutersToken extends MultiToken<ExpressRouter> {}\n\n/**\n * DI MultiToken for registering and resolving global Express middlewares.\n */\nexport class ExpressMiddlewaresToken extends MultiToken<ExpressMiddleware> {}\n\n/**\n * DI MultiToken for registering and resolving global Express error handlers.\n */\nexport class ExpressErrorHandlersToken extends MultiToken<ExpressErrorHandler> {}\n","import type { Server } from \"node:http\";\nimport express, {\n Router,\n type Express,\n type RequestHandler,\n type ErrorRequestHandler,\n} from \"express\";\nimport { MakeInjectable, type DepsType } from \"@solid-stack/di\";\nimport { IPresentation, ILogger } from \"@solid-stack/agnos\";\nimport {\n ExpressPxConfigsToken,\n ExpressRoutersToken,\n ExpressMiddlewaresToken,\n ExpressErrorHandlersToken,\n} from \"../types/index.js\";\n\n/**\n * Express HTTP Presentation Layer implementation for the Agnos framework.\n *\n * Implements the IPresentation lifecycle contract:\n * - init: Configures JSON body parsing, attaches global middlewares, routers, and error handlers.\n * - run: Starts the HTTP server on the configured port and host.\n * - stop: Gracefully shuts down the HTTP server.\n */\n@MakeInjectable\nexport class ExpressPresentation implements IPresentation {\n private readonly app: Express;\n private server: Server | null = null;\n\n public static deps = {\n configs: ExpressPxConfigsToken,\n errorHandlers: ExpressErrorHandlersToken,\n middlewares: ExpressMiddlewaresToken,\n routers: ExpressRoutersToken,\n logger: ILogger,\n };\n\n constructor(public deps: DepsType<typeof ExpressPresentation.deps>) {\n this.app = express();\n }\n\n /**\n * Initializes middleware, feature routers, and error handlers on the Express app.\n */\n async init(): Promise<void> {\n this.app.use(express.json());\n\n this.attachMiddlewares();\n this.attachRouters();\n this.attachErrorHandlers();\n }\n\n private attachMiddlewares(): void {\n const middlewares = this.deps.middlewares ?? [];\n for (const middleware of middlewares) {\n this.app.use((req, res, next) => middleware.handler(req, res, next));\n }\n }\n\n private attachRouters(): void {\n const routers = this.deps.routers ?? [];\n this.deps.logger.debug(\"Registering Express routers...\", {\n count: routers.length,\n });\n\n for (const subRouter of routers) {\n const router = Router();\n\n // Attach router-level middlewares if present\n if (subRouter.middlewares && subRouter.middlewares.length > 0) {\n for (const mw of subRouter.middlewares) {\n router.use(mw);\n }\n }\n\n // Attach individual routes\n for (const route of subRouter.routes) {\n this.deps.logger.debug(\n `Registering route: [${route.method.toUpperCase()}] ${subRouter.path}${route.path}`,\n );\n\n const routeHandler: RequestHandler = (req, res, next) =>\n route.handler(req, res, next);\n\n if (route.middlewares && route.middlewares.length > 0) {\n router[route.method](route.path, ...route.middlewares, routeHandler);\n } else {\n router[route.method](route.path, routeHandler);\n }\n }\n\n this.app.use(subRouter.path, router);\n }\n }\n\n private attachErrorHandlers(): void {\n const errorHandlers = this.deps.errorHandlers ?? [];\n for (const errorHandler of errorHandlers) {\n const handler: ErrorRequestHandler = (err, req, res, next) =>\n errorHandler.handler(err, req, res, next);\n this.app.use(handler);\n }\n }\n\n /**\n * Starts the Express server listening on the configured port and host.\n */\n async run(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n const { port, host } = this.deps.configs;\n\n const onListening = () => {\n this.deps.logger.info(\n `Express server listening on ${host ? `${host}:` : \"port \"}${port}`,\n );\n resolve();\n };\n\n if (host) {\n this.server = this.app.listen(port, host, onListening);\n } else {\n this.server = this.app.listen(port, onListening);\n }\n\n this.server.on(\"error\", (err: Error) => {\n this.deps.logger.error(\"Express server error:\", err);\n reject(err);\n });\n } catch (err) {\n reject(err);\n }\n });\n }\n\n /**\n * Gracefully closes the running HTTP server.\n */\n async stop(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!this.server) {\n return resolve();\n }\n\n this.server.close((err) => {\n if (err) {\n this.deps.logger.error(\"Error closing Express server:\", err);\n return reject(err);\n }\n this.deps.logger.info(\"Express server stopped receiving new requests.\");\n this.server = null;\n resolve();\n });\n });\n }\n\n /**\n * Returns the underlying Express application instance.\n */\n public getApp(): Express {\n return this.app;\n }\n\n /**\n * Returns the active Node HTTP server instance, or null if not currently running.\n */\n public getServer(): Server | null {\n return this.server;\n }\n}\n","import { stat } from \"node:fs/promises\";\n\n/**\n * Checks if a given filesystem path exists and is a directory.\n *\n * @param dir - The directory path to check\n * @returns True if the directory exists, false if it does not exist\n */\nexport async function folderExists(dir: string): Promise<boolean> {\n try {\n const stats = await stat(dir);\n return stats.isDirectory();\n } catch (error: any) {\n if (error?.code === \"ENOENT\") {\n return false;\n }\n throw error;\n }\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type { Container } from \"@solid-stack/di\";\nimport { PresentationError } from \"@solid-stack/agnos\";\nimport {\n ExpressMiddlewaresToken,\n ExpressErrorHandlersToken,\n} from \"../types/index.js\";\nimport { folderExists } from \"./folderExists.js\";\n\n/**\n * Loads global middlewares and error handlers from the main presentation directory.\n *\n * @param c - DI Container\n * @param mainDir - Directory containing main presentation index file\n */\nexport async function autoLoadMainPx(\n c: Container,\n mainDir: string,\n): Promise<void> {\n if (!(await folderExists(mainDir))) {\n return;\n }\n\n const mainDirContents = await fs.readdir(mainDir, { withFileTypes: true });\n\n const indexFile = mainDirContents.find(\n (f) =>\n f.isFile() &&\n (f.name === \"index.ts\" ||\n f.name === \"index.js\" ||\n f.name === \"index.mjs\" ||\n f.name === \"index.cjs\"),\n );\n\n if (!indexFile) {\n throw new PresentationError(\n `No index.ts or index.js file found in the main presentation directory: ${mainDir}`,\n { phase: \"init\" },\n );\n }\n\n const indexFilePath = path.join(mainDir, indexFile.name);\n const imported = await import(pathToFileURL(indexFilePath).href);\n\n // Register middlewares if exported\n const middlewares = imported.middlewares;\n if (middlewares && Array.isArray(middlewares)) {\n for (const middleware of middlewares) {\n c.provideMulti(ExpressMiddlewaresToken, middleware);\n }\n }\n\n // Register error handlers if exported\n const errorHandlers = imported.errorHandlers;\n if (errorHandlers && Array.isArray(errorHandlers)) {\n for (const errorHandler of errorHandlers) {\n c.provideMulti(ExpressErrorHandlersToken, errorHandler);\n }\n }\n}\n","import type { RequestHandler } from \"express\";\nimport type { ExpressRoute } from \"./ExpressRoute.js\";\n\n/**\n * Abstract base class for defining an Express router grouping multiple routes under a common path prefix.\n */\nexport abstract class ExpressRouter {\n /**\n * The base URL path prefix for this router (e.g. '/users' or '/api/v1/orders').\n */\n public abstract get path(): string;\n\n /**\n * The collection of routes registered under this router.\n */\n public abstract get routes(): ExpressRoute[];\n\n /**\n * Optional router-level middlewares executed for all routes in this router.\n */\n public middlewares?: RequestHandler[];\n}\n","import type { RequestHandler } from \"express\";\nimport type { ExpressRouteMethod } from \"../types/index.js\";\n\n/**\n * Abstract base class for defining an Express route handler within an Agnos feature.\n */\nexport abstract class ExpressRoute {\n /**\n * The HTTP method for this route (e.g. 'get', 'post', 'put', 'delete').\n */\n abstract method: ExpressRouteMethod;\n\n /**\n * The sub-path for this route relative to the feature router's base path (e.g. '/' or '/:id').\n */\n abstract path: string;\n\n /**\n * The Express request handler function.\n */\n abstract handler: RequestHandler;\n\n /**\n * Optional route-level middlewares executed before handler.\n */\n middlewares?: RequestHandler[];\n}\n","/**\n * Default configuration constants for Agnos Express presentation.\n */\n\nexport const DEFAULT_PORT = 3000;\nexport const DEFAULT_HOST = \"0.0.0.0\";\nexport const DEFAULT_FEATURE_PX_DIR = \"pxExpress\";\nexport const DEFAULT_HANDLERS_DIR = \"handlers\";\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport {\n MakeInjectable,\n registerInjectable,\n diFactoryRegistry,\n type Container,\n type DepsType,\n} from \"@solid-stack/di\";\nimport { FeatureLoadError } from \"@solid-stack/agnos\";\nimport { ExpressRoutersToken } from \"../types/index.js\";\nimport { ExpressRouter } from \"../core/ExpressRouter.js\";\nimport { ExpressRoute } from \"../core/ExpressRoute.js\";\nimport { folderExists } from \"./folderExists.js\";\nimport {\n DEFAULT_FEATURE_PX_DIR,\n DEFAULT_HANDLERS_DIR,\n} from \"../constants/index.js\";\n\n/**\n * Autoloads Express presentation modules from feature directories.\n *\n * For each feature directory:\n * 1. Checks for 'pxExpress' folder.\n * 2. Checks for index.ts/js exporting 'path' (string prefix).\n * 3. Scans 'handlers' folder for ExpressRoute implementations.\n * 4. Dynamically builds a FeatureRouter class registered with ExpressRoutersToken.\n *\n * @param c - DI Container\n * @param featuresDir - Path to features directory\n */\nexport async function autoLoadFeature(\n c: Container,\n featuresDir: string,\n): Promise<void> {\n if (!(await folderExists(featuresDir))) {\n return;\n }\n\n const features = await fs.readdir(featuresDir, { withFileTypes: true });\n\n for (const feature of features) {\n if (!feature.isDirectory()) continue;\n\n const featureName = feature.name;\n const pxExpressDir = path.join(\n featuresDir,\n featureName,\n DEFAULT_FEATURE_PX_DIR,\n );\n\n if (!(await folderExists(pxExpressDir))) continue;\n\n const pxExpressDirFiles = await fs.readdir(pxExpressDir);\n const indexFile = pxExpressDirFiles.find(\n (f) =>\n f === \"index.ts\" ||\n f === \"index.js\" ||\n f === \"index.mjs\" ||\n f === \"index.cjs\",\n );\n\n if (!indexFile) {\n throw new FeatureLoadError(\n featureName,\n pxExpressDir,\n `The express presentation of feature \"${featureName}\" (${pxExpressDir}) does not have an index.ts or index.js file.`,\n );\n }\n\n const indexFilePath = path.join(pxExpressDir, indexFile);\n const imported = await import(pathToFileURL(indexFilePath).href);\n\n const routePath = imported.path;\n\n if (typeof routePath !== \"string\") {\n throw new FeatureLoadError(\n featureName,\n indexFilePath,\n `The express presentation of feature \"${featureName}\" (${pxExpressDir}) has an index file, but it doesn't export a string named 'path'.`,\n );\n }\n\n // Load routes from handlers/\n const routeClasses: {\n meta: { name: string };\n class: new (...args: any[]) => ExpressRoute;\n }[] = [];\n\n const routesDir = path.join(pxExpressDir, DEFAULT_HANDLERS_DIR);\n\n if (await folderExists(routesDir)) {\n const routeFiles = await fs.readdir(routesDir);\n\n for (const routeFile of routeFiles) {\n if (\n !routeFile.match(/\\.(js|ts|mjs|cjs)$/) ||\n routeFile.endsWith(\".d.ts\") ||\n routeFile.includes(\".test.\") ||\n routeFile.includes(\".spec.\")\n ) {\n continue;\n }\n\n const routeFilePath = path.join(routesDir, routeFile);\n const routeFileModule = await import(pathToFileURL(routeFilePath).href);\n const routeClass = routeFileModule.default;\n\n if (!routeClass) {\n throw new FeatureLoadError(\n featureName,\n routeFilePath,\n `The route file \"${routeFile}\" in feature \"${featureName}\" doesn't have a default export.`,\n );\n }\n\n if (\n typeof routeClass !== \"function\" ||\n !(routeClass.prototype instanceof ExpressRoute)\n ) {\n throw new FeatureLoadError(\n featureName,\n routeFilePath,\n `The default export in \"${routeFile}\" (feature: \"${featureName}\") does not inherit from ExpressRoute.`,\n );\n }\n\n // Ensure DI factory is registered for the route class\n if (!diFactoryRegistry.has(routeClass)) {\n if ((routeClass as any).deps) {\n registerInjectable(routeClass as any);\n } else {\n diFactoryRegistry.set(\n routeClass,\n () => new (routeClass as any)(),\n );\n }\n }\n\n routeClasses.push({\n meta: { name: routeClass.name },\n class: routeClass,\n });\n }\n }\n\n // Build the dependency object for DI resolution\n const featureDeps = routeClasses.reduce(\n (acc, entry) => {\n const depKey =\n entry.meta.name.charAt(0).toLowerCase() + entry.meta.name.slice(1);\n acc[depKey] = entry.class;\n return acc;\n },\n {} as Record<string, new (...args: any[]) => ExpressRoute>,\n );\n\n const optionalMiddlewares = Array.isArray(imported.middlewares)\n ? imported.middlewares\n : undefined;\n\n // Create the router class dynamically\n @MakeInjectable\n class FeatureRouter extends ExpressRouter {\n public static deps = featureDeps;\n public middlewares = optionalMiddlewares;\n\n constructor(public deps: DepsType<typeof featureDeps>) {\n super();\n }\n\n public get path(): string {\n return routePath;\n }\n\n public get routes(): ExpressRoute[] {\n return Object.values(this.deps) as ExpressRoute[];\n }\n }\n\n const routerClassName = `${\n featureName.charAt(0).toUpperCase() + featureName.slice(1)\n }ExpressRouter`;\n\n Object.defineProperty(FeatureRouter, \"name\", {\n value: routerClassName,\n });\n\n c.provideMulti(ExpressRoutersToken, FeatureRouter);\n }\n}\n","import type { Container } from \"@solid-stack/di\";\nimport { IPresentation } from \"@solid-stack/agnos\";\nimport { ExpressPresentation } from \"./ExpressPresentation.js\";\nimport {\n ExpressPxConfigsToken,\n type ExpressPresentationOptions,\n} from \"../types/index.js\";\nimport { autoLoadMainPx } from \"../utils/autoLoadMainPx.js\";\nimport { autoLoadFeature } from \"../utils/autoLoadFeature.js\";\nimport { DEFAULT_PORT } from \"../constants/index.js\";\n\n/**\n * Bootstraps and registers the Express presentation layer in the DI container.\n *\n * Configures:\n * 1. ExpressPxConfigsToken with port and optional host.\n * 2. Autoloads global middlewares and error handlers from mainDir if specified.\n * 3. Autoloads feature routes from featuresDir if specified.\n * 4. Binds IPresentation to ExpressPresentation.\n *\n * @param container - The @solid-stack/di Container instance.\n * @param options - Presentation bootstrap options.\n */\nexport const loadExpressPresentation = async (\n container: Container,\n options?: ExpressPresentationOptions,\n): Promise<void> => {\n const port = options?.port ?? DEFAULT_PORT;\n const host = options?.host;\n\n container.provideValue(ExpressPxConfigsToken, {\n port,\n ...(host ? { host } : {}),\n });\n\n if (options?.mainDir) {\n await autoLoadMainPx(container, options.mainDir);\n }\n\n if (options?.featuresDir) {\n await autoLoadFeature(container, options.featuresDir);\n }\n\n container.provide(IPresentation, ExpressPresentation);\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ValueToken, MultiToken } from "@solid-stack/di";
|
|
2
|
+
import type { RequestHandler, ErrorRequestHandler } from "express";
|
|
3
|
+
import type { ExpressRouter } from "../core/ExpressRouter.js";
|
|
4
|
+
/**
|
|
5
|
+
* Express Presentation configuration settings.
|
|
6
|
+
*/
|
|
7
|
+
export interface ExpressPxConfigs {
|
|
8
|
+
/**
|
|
9
|
+
* The port on which the Express HTTP server should listen.
|
|
10
|
+
* @default 3000
|
|
11
|
+
*/
|
|
12
|
+
port: number;
|
|
13
|
+
/**
|
|
14
|
+
* Optional host interface to bind the server to (e.g. '0.0.0.0' or 'localhost').
|
|
15
|
+
*/
|
|
16
|
+
host?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Options for configuring and bootstrapping Express presentation via loadExpressPresentation.
|
|
20
|
+
*/
|
|
21
|
+
export interface ExpressPresentationOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Port number for the Express server.
|
|
24
|
+
* @default 3000
|
|
25
|
+
*/
|
|
26
|
+
port?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Optional host interface to bind the server to.
|
|
29
|
+
*/
|
|
30
|
+
host?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Absolute or relative path to the main presentation directory containing global middlewares and error handlers.
|
|
33
|
+
*/
|
|
34
|
+
mainDir?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Absolute or relative path to the features directory containing modular feature folders with Express route handlers.
|
|
37
|
+
*/
|
|
38
|
+
featuresDir?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Express route HTTP methods supported.
|
|
42
|
+
*/
|
|
43
|
+
export type ExpressRouteMethod = "get" | "post" | "put" | "patch" | "delete" | "options" | "head" | "all";
|
|
44
|
+
/**
|
|
45
|
+
* Interface contract for global Express middlewares.
|
|
46
|
+
*/
|
|
47
|
+
export interface ExpressMiddleware {
|
|
48
|
+
handler: RequestHandler;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Interface contract for global Express error handlers.
|
|
52
|
+
*/
|
|
53
|
+
export interface ExpressErrorHandler {
|
|
54
|
+
handler: ErrorRequestHandler;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* DI ValueToken for injecting Express Presentation configuration.
|
|
58
|
+
*/
|
|
59
|
+
export declare class ExpressPxConfigsToken extends ValueToken<ExpressPxConfigs> {
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* DI MultiToken for registering and resolving Express routers.
|
|
63
|
+
*/
|
|
64
|
+
export declare class ExpressRoutersToken extends MultiToken<ExpressRouter> {
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* DI MultiToken for registering and resolving global Express middlewares.
|
|
68
|
+
*/
|
|
69
|
+
export declare class ExpressMiddlewaresToken extends MultiToken<ExpressMiddleware> {
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* DI MultiToken for registering and resolving global Express error handlers.
|
|
73
|
+
*/
|
|
74
|
+
export declare class ExpressErrorHandlersToken extends MultiToken<ExpressErrorHandler> {
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=public.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/types/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B,KAAK,GACL,MAAM,GACN,KAAK,GACL,OAAO,GACP,QAAQ,GACR,SAAS,GACT,MAAM,GACN,KAAK,CAAC;AAEV;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,cAAc,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,UAAU,CAAC,gBAAgB,CAAC;CAAG;AAE1E;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,UAAU,CAAC,aAAa,CAAC;CAAG;AAErE;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,UAAU,CAAC,iBAAiB,CAAC;CAAG;AAE7E;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,UAAU,CAAC,mBAAmB,CAAC;CAAG"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Container } from "@solid-stack/di";
|
|
2
|
+
/**
|
|
3
|
+
* Autoloads Express presentation modules from feature directories.
|
|
4
|
+
*
|
|
5
|
+
* For each feature directory:
|
|
6
|
+
* 1. Checks for 'pxExpress' folder.
|
|
7
|
+
* 2. Checks for index.ts/js exporting 'path' (string prefix).
|
|
8
|
+
* 3. Scans 'handlers' folder for ExpressRoute implementations.
|
|
9
|
+
* 4. Dynamically builds a FeatureRouter class registered with ExpressRoutersToken.
|
|
10
|
+
*
|
|
11
|
+
* @param c - DI Container
|
|
12
|
+
* @param featuresDir - Path to features directory
|
|
13
|
+
*/
|
|
14
|
+
export declare function autoLoadFeature(c: Container, featuresDir: string): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=autoLoadFeature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autoLoadFeature.d.ts","sourceRoot":"","sources":["../../src/utils/autoLoadFeature.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,SAAS,EAEf,MAAM,iBAAiB,CAAC;AAWzB;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACnC,CAAC,EAAE,SAAS,EACZ,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CA4Jf"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Container } from "@solid-stack/di";
|
|
2
|
+
/**
|
|
3
|
+
* Loads global middlewares and error handlers from the main presentation directory.
|
|
4
|
+
*
|
|
5
|
+
* @param c - DI Container
|
|
6
|
+
* @param mainDir - Directory containing main presentation index file
|
|
7
|
+
*/
|
|
8
|
+
export declare function autoLoadMainPx(c: Container, mainDir: string): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=autoLoadMainPx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autoLoadMainPx.d.ts","sourceRoot":"","sources":["../../src/utils/autoLoadMainPx.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAQjD;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,CAAC,EAAE,SAAS,EACZ,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAyCf"}
|