@vercube/core 0.0.47 → 1.0.0-beta.1
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/dist/index.d.mts +580 -170
- package/dist/index.mjs +342 -39
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { createReadStream } from "node:fs";
|
|
|
6
6
|
import { stat } from "node:fs/promises";
|
|
7
7
|
import { extname, join, normalize } from "node:path";
|
|
8
8
|
import { BaseLogger, Logger } from "@vercube/logger";
|
|
9
|
-
import {
|
|
9
|
+
import { createMiddlewareLogger, extractSafeHeaders } from "@vercube/logger/toolkit";
|
|
10
10
|
import { loadConfig, setupDotenv } from "c12";
|
|
11
11
|
import { defu } from "defu";
|
|
12
12
|
//#region src/Services/Config/RuntimeConfig.ts
|
|
@@ -336,12 +336,24 @@ var MetadataResolver = class {
|
|
|
336
336
|
* @public
|
|
337
337
|
*/
|
|
338
338
|
async resolveArgs(args, event) {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
339
|
+
let list = args;
|
|
340
|
+
if (args.length > 1) {
|
|
341
|
+
for (let i = 1; i < args.length; i++) if (args[i - 1].idx > args[i].idx) {
|
|
342
|
+
list = [...args].sort((a, b) => a.idx - b.idx);
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const resolvedArgs = [];
|
|
347
|
+
for (let i = 0; i < list.length; i++) {
|
|
348
|
+
const arg = list[i];
|
|
349
|
+
let resolved = this.resolveArg(arg, event);
|
|
350
|
+
if (resolved instanceof Promise) resolved = await resolved;
|
|
351
|
+
resolvedArgs.push({
|
|
352
|
+
...arg,
|
|
353
|
+
resolved
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
return resolvedArgs;
|
|
345
357
|
}
|
|
346
358
|
/**
|
|
347
359
|
* Resolves an argument for a given event.
|
|
@@ -523,7 +535,7 @@ var RequestContext = class {
|
|
|
523
535
|
}
|
|
524
536
|
};
|
|
525
537
|
//#endregion
|
|
526
|
-
//#region \0@oxc-project+runtime@0.
|
|
538
|
+
//#region \0@oxc-project+runtime@0.134.0/helpers/esm/decorate.js
|
|
527
539
|
function __decorate(decorators, target, key, desc) {
|
|
528
540
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
529
541
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -559,7 +571,15 @@ var RequestHandler = class {
|
|
|
559
571
|
const method = this.gMetadataResolver.resolveMethod(prototype, propertyName);
|
|
560
572
|
const middlewares = this.gMetadataResolver.resolveMiddlewares(prototype, propertyName);
|
|
561
573
|
const globalMiddlewares = this.gGlobalMiddlewareRegistry.middlewares;
|
|
562
|
-
const
|
|
574
|
+
const combined = [...middlewares, ...globalMiddlewares];
|
|
575
|
+
const seen = /* @__PURE__ */ new Set();
|
|
576
|
+
const uniqueMiddlewares = [];
|
|
577
|
+
for (const m of combined) {
|
|
578
|
+
if (seen.has(m.middleware)) continue;
|
|
579
|
+
seen.add(m.middleware);
|
|
580
|
+
uniqueMiddlewares.push(m);
|
|
581
|
+
}
|
|
582
|
+
const resolvedMiddlewares = uniqueMiddlewares.map((m) => ({
|
|
563
583
|
...m,
|
|
564
584
|
middleware: this.gContainer.resolve(m.middleware)
|
|
565
585
|
}));
|
|
@@ -570,7 +590,7 @@ var RequestHandler = class {
|
|
|
570
590
|
return {
|
|
571
591
|
instance,
|
|
572
592
|
propertyName,
|
|
573
|
-
args: method.args,
|
|
593
|
+
args: method.args.length <= 1 ? method.args : [...method.args].sort((a, b) => a.idx - b.idx),
|
|
574
594
|
middlewares: {
|
|
575
595
|
beforeMiddlewares,
|
|
576
596
|
afterMiddlewares
|
|
@@ -785,7 +805,7 @@ var RequestHandler = class {
|
|
|
785
805
|
async executeMiddlewares(middlewares, options) {
|
|
786
806
|
const { request, response, methodArgs, handlerResponse, executeRequest, executeResponse } = options;
|
|
787
807
|
let currentResponse = response;
|
|
788
|
-
for
|
|
808
|
+
for (const hook of middlewares) try {
|
|
789
809
|
if (executeRequest) {
|
|
790
810
|
const requestResult = await this.executeMiddlewareRequest(hook, request, currentResponse, methodArgs);
|
|
791
811
|
if (requestResult instanceof Response) return {
|
|
@@ -1039,11 +1059,11 @@ var Router = class {
|
|
|
1039
1059
|
* @returns {RouterTypes.RouteMatched<RouterTypes.RouterHandler> | undefined} The matched route or undefined if no match found
|
|
1040
1060
|
*/
|
|
1041
1061
|
resolve(route) {
|
|
1042
|
-
let
|
|
1043
|
-
try {
|
|
1044
|
-
|
|
1062
|
+
let pathname = route.path;
|
|
1063
|
+
if (/^https?:\/\//i.test(pathname)) try {
|
|
1064
|
+
pathname = new URL(pathname).pathname;
|
|
1045
1065
|
} catch {}
|
|
1046
|
-
return findRoute(this.fRouterContext, route.method.toUpperCase(),
|
|
1066
|
+
return findRoute(this.fRouterContext, route.method.toUpperCase(), pathname);
|
|
1047
1067
|
}
|
|
1048
1068
|
};
|
|
1049
1069
|
__decorate([Inject(HooksService)], Router.prototype, "gHooksService", void 0);
|
|
@@ -1225,8 +1245,9 @@ var HttpServer = class {
|
|
|
1225
1245
|
*/
|
|
1226
1246
|
async handleRequest(request) {
|
|
1227
1247
|
try {
|
|
1248
|
+
const pathname = new URL(request.url).pathname;
|
|
1228
1249
|
const route = this.gRouter.resolve({
|
|
1229
|
-
path:
|
|
1250
|
+
path: pathname,
|
|
1230
1251
|
method: request.method
|
|
1231
1252
|
});
|
|
1232
1253
|
if (!route && request.method === "OPTIONS") return this.gRequestHandler.handlePreflight(request);
|
|
@@ -1248,17 +1269,19 @@ __decorate([Inject(StaticRequestHandler)], HttpServer.prototype, "gStaticRequest
|
|
|
1248
1269
|
//#endregion
|
|
1249
1270
|
//#region src/Services/Plugins/BasePlugin.ts
|
|
1250
1271
|
/**
|
|
1251
|
-
*
|
|
1272
|
+
* Base class for class-based plugins registered via `defineConfig({ plugins })` or `app.addPlugin()`.
|
|
1273
|
+
*
|
|
1274
|
+
* @typeParam T - Options type passed into `configure`, `setup` / `use`, and `hooks` when provided.
|
|
1252
1275
|
*/
|
|
1253
1276
|
var BasePlugin = class {
|
|
1254
|
-
/**
|
|
1255
|
-
* The name of the plugin.
|
|
1256
|
-
*/
|
|
1277
|
+
/** Stable identifier used by the plugin registry and logging. */
|
|
1257
1278
|
name;
|
|
1258
1279
|
/**
|
|
1259
|
-
*
|
|
1260
|
-
*
|
|
1261
|
-
* @
|
|
1280
|
+
* Legacy runtime attach when the plugin is not registered with a `setup` implementation.
|
|
1281
|
+
*
|
|
1282
|
+
* @param app - Running application instance.
|
|
1283
|
+
* @param options - Optional plugin options from registration or config tuple.
|
|
1284
|
+
* @returns Void or a promise that settles when attachment is done.
|
|
1262
1285
|
*/
|
|
1263
1286
|
use(app, options) {}
|
|
1264
1287
|
};
|
|
@@ -1389,12 +1412,20 @@ var App = class {
|
|
|
1389
1412
|
return this.gHttpServer.handleRequest(request);
|
|
1390
1413
|
}
|
|
1391
1414
|
/**
|
|
1392
|
-
*
|
|
1415
|
+
* Initializes registry plugins, then runs `setup` on each normalized config plugin in order.
|
|
1393
1416
|
*
|
|
1394
|
-
* @
|
|
1417
|
+
* @returns Resolves when all plugin `setup` hooks have been awaited.
|
|
1395
1418
|
*/
|
|
1396
1419
|
async resolvePlugins() {
|
|
1397
1420
|
await this.gPluginsRegistry.init(this);
|
|
1421
|
+
const plugins = this.fConfig.plugins;
|
|
1422
|
+
if (!plugins?.length) return;
|
|
1423
|
+
const env = {
|
|
1424
|
+
cwd: typeof process === "undefined" ? "." : process.cwd(),
|
|
1425
|
+
dev: this.fConfig.dev,
|
|
1426
|
+
production: this.fConfig.production
|
|
1427
|
+
};
|
|
1428
|
+
for (const plugin of plugins) await plugin.setup?.(this, env);
|
|
1398
1429
|
}
|
|
1399
1430
|
};
|
|
1400
1431
|
__decorate([Inject(Router)], App.prototype, "gRouter", void 0);
|
|
@@ -1403,6 +1434,68 @@ __decorate([Inject(HttpServer)], App.prototype, "gHttpServer", void 0);
|
|
|
1403
1434
|
__decorate([Inject(StaticRequestHandler)], App.prototype, "gStaticRequestHandler", void 0);
|
|
1404
1435
|
__decorate([Inject(RuntimeConfig)], App.prototype, "gRuntimeConfig", void 0);
|
|
1405
1436
|
//#endregion
|
|
1437
|
+
//#region src/Middleware/EvlogMiddleware.ts
|
|
1438
|
+
/**
|
|
1439
|
+
* Key under which the request-scoped evlog logger is stored in {@link RequestContext}.
|
|
1440
|
+
*/
|
|
1441
|
+
const EVLOG_REQUEST_LOGGER_KEY = "evlog:requestLogger";
|
|
1442
|
+
/**
|
|
1443
|
+
* Key under which the evlog `finish` callback is stored in {@link RequestContext}.
|
|
1444
|
+
*/
|
|
1445
|
+
const EVLOG_FINISH_KEY = "evlog:finish";
|
|
1446
|
+
/**
|
|
1447
|
+
* Global middleware that emits one structured wide event per request using
|
|
1448
|
+
* evlog (https://evlog.dev).
|
|
1449
|
+
*
|
|
1450
|
+
* On each request it creates a request-scoped logger via evlog's toolkit and
|
|
1451
|
+
* stores it in {@link RequestContext} (so handlers can enrich the event), then
|
|
1452
|
+
* emits the accumulated wide event - including method, path, status and
|
|
1453
|
+
* duration - when the response is produced.
|
|
1454
|
+
*
|
|
1455
|
+
* Registered automatically by the framework; disable via `requestLogging: false`
|
|
1456
|
+
* in the application config.
|
|
1457
|
+
*/
|
|
1458
|
+
var EvlogMiddleware = class extends BaseMiddleware {
|
|
1459
|
+
/**
|
|
1460
|
+
* Request context used to carry the request-scoped logger across the lifecycle.
|
|
1461
|
+
*/
|
|
1462
|
+
gRequestContext;
|
|
1463
|
+
/**
|
|
1464
|
+
* Creates a request-scoped evlog logger and stores it in the request context.
|
|
1465
|
+
*
|
|
1466
|
+
* @param request - The incoming HTTP request
|
|
1467
|
+
* @param _response - The current response (unused)
|
|
1468
|
+
* @param args - Middleware options (evlog include/exclude/drain/enrich/keep)
|
|
1469
|
+
*/
|
|
1470
|
+
onRequest(request, _response, args) {
|
|
1471
|
+
if (!this.gRequestContext) return;
|
|
1472
|
+
const options = args?.middlewareArgs ?? {};
|
|
1473
|
+
const url = new URL(request.url);
|
|
1474
|
+
const { logger, finish, skipped } = createMiddlewareLogger({
|
|
1475
|
+
method: request.method,
|
|
1476
|
+
path: url.pathname,
|
|
1477
|
+
requestId: request.headers.get("x-request-id") ?? crypto.randomUUID(),
|
|
1478
|
+
headers: extractSafeHeaders(request.headers),
|
|
1479
|
+
...options
|
|
1480
|
+
});
|
|
1481
|
+
if (skipped) return;
|
|
1482
|
+
this.gRequestContext.set(EVLOG_REQUEST_LOGGER_KEY, logger);
|
|
1483
|
+
this.gRequestContext.set(EVLOG_FINISH_KEY, finish);
|
|
1484
|
+
}
|
|
1485
|
+
/**
|
|
1486
|
+
* Emits the request wide event with the final response status.
|
|
1487
|
+
*
|
|
1488
|
+
* @param _request - The HTTP request (unused)
|
|
1489
|
+
* @param response - The final HTTP response
|
|
1490
|
+
*/
|
|
1491
|
+
async onResponse(_request, response) {
|
|
1492
|
+
if (!this.gRequestContext) return;
|
|
1493
|
+
const finish = this.gRequestContext.get(EVLOG_FINISH_KEY);
|
|
1494
|
+
if (finish) await finish({ status: response.status }).catch(() => {});
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
__decorate([InjectOptional(RequestContext)], EvlogMiddleware.prototype, "gRequestContext", void 0);
|
|
1498
|
+
//#endregion
|
|
1406
1499
|
//#region src/Errors/Http/InternalServerError.ts
|
|
1407
1500
|
/**
|
|
1408
1501
|
* Represents an Internal Server error (HTTP 500).
|
|
@@ -1488,13 +1581,7 @@ function createContainer(config) {
|
|
|
1488
1581
|
const container = new Container();
|
|
1489
1582
|
container.bindInstance(Container, container);
|
|
1490
1583
|
container.bind(Logger, BaseLogger);
|
|
1491
|
-
container.get(Logger).configure({
|
|
1492
|
-
logLevel: config.logLevel ?? "debug",
|
|
1493
|
-
providers: [{
|
|
1494
|
-
name: "console",
|
|
1495
|
-
provider: ConsoleProvider
|
|
1496
|
-
}]
|
|
1497
|
-
});
|
|
1584
|
+
container.get(Logger).configure({ logLevel: config.logLevel ?? "debug" });
|
|
1498
1585
|
container.bind(ErrorHandlerProvider, DefaultErrorHandlerProvider);
|
|
1499
1586
|
container.bind(HttpServer);
|
|
1500
1587
|
container.bind(StaticRequestHandler);
|
|
@@ -1507,9 +1594,212 @@ function createContainer(config) {
|
|
|
1507
1594
|
container.bind(GlobalMiddlewareRegistry);
|
|
1508
1595
|
container.bind(RequestContext);
|
|
1509
1596
|
container.bind(ValidationProvider, StandardSchemaValidationProvider);
|
|
1597
|
+
if (config.requestLogging !== false) container.get(GlobalMiddlewareRegistry).registerGlobalMiddleware(EvlogMiddleware);
|
|
1510
1598
|
return container;
|
|
1511
1599
|
}
|
|
1512
1600
|
//#endregion
|
|
1601
|
+
//#region src/Plugins/VercubePlugin.ts
|
|
1602
|
+
/**
|
|
1603
|
+
* Tracks cleanup callbacks for plugin-registered dev hooks per hookable instance.
|
|
1604
|
+
* This prevents listener accumulation across config reloads in `vercube dev`.
|
|
1605
|
+
*/
|
|
1606
|
+
const pluginDevHookCleanupMap = /* @__PURE__ */ new WeakMap();
|
|
1607
|
+
/**
|
|
1608
|
+
* Returns the same plugin object; narrows the type for object literals.
|
|
1609
|
+
*
|
|
1610
|
+
* @param plugin - Plugin definition with optional hooks.
|
|
1611
|
+
* @returns The input plugin unchanged.
|
|
1612
|
+
*/
|
|
1613
|
+
function defineVercubePlugin(plugin) {
|
|
1614
|
+
return plugin;
|
|
1615
|
+
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Builds a `[PluginClass, options]` tuple with options typed from the class `BasePlugin` generic.
|
|
1618
|
+
*
|
|
1619
|
+
* @param PluginClass - Constructor extending `BasePlugin<TOptions>`.
|
|
1620
|
+
* @param options - Options object matching `TOptions` for that class.
|
|
1621
|
+
* @returns Tuple consumed by the config `plugins` pipeline.
|
|
1622
|
+
*/
|
|
1623
|
+
function withPluginOptions(PluginClass, options) {
|
|
1624
|
+
return [PluginClass, options];
|
|
1625
|
+
}
|
|
1626
|
+
/**
|
|
1627
|
+
* @param value - Any value to test.
|
|
1628
|
+
* @returns True if `value` is a constructor whose prototype extends `BasePlugin`.
|
|
1629
|
+
*/
|
|
1630
|
+
function isBasePluginClass(value) {
|
|
1631
|
+
return typeof value === "function" && value.prototype instanceof BasePlugin;
|
|
1632
|
+
}
|
|
1633
|
+
/**
|
|
1634
|
+
* Adapts a `BasePlugin` subclass into a `VercubePlugin` for `defineConfig({ plugins })`.
|
|
1635
|
+
*
|
|
1636
|
+
* @param PluginClass - Plugin class constructor.
|
|
1637
|
+
* @param options - Optional value passed into `configure`, `setup` / `use`, and `hooks`.
|
|
1638
|
+
* @param displayName - Optional name; defaults to `PluginClass.name`.
|
|
1639
|
+
* @returns Object implementing `VercubePlugin` that delegates to the class.
|
|
1640
|
+
*/
|
|
1641
|
+
function vercubePluginFromClass(PluginClass, options, displayName) {
|
|
1642
|
+
return {
|
|
1643
|
+
name: displayName ?? PluginClass.name,
|
|
1644
|
+
config: async (cfg, _env) => {
|
|
1645
|
+
const sample = new PluginClass();
|
|
1646
|
+
const configure = sample.configure;
|
|
1647
|
+
if (typeof configure === "function") return configure.call(sample, cfg, options);
|
|
1648
|
+
},
|
|
1649
|
+
setup: async (app) => {
|
|
1650
|
+
const instance = app.container.resolve(PluginClass);
|
|
1651
|
+
if (typeof instance.setup === "function") return instance.setup.call(instance, app, options);
|
|
1652
|
+
return instance.use(app, options);
|
|
1653
|
+
},
|
|
1654
|
+
cli: async (ctx) => {
|
|
1655
|
+
const sample = new PluginClass();
|
|
1656
|
+
if (typeof sample.setupCLI === "function") return sample.setupCLI.call(sample, ctx);
|
|
1657
|
+
},
|
|
1658
|
+
hooks: async (ctx) => {
|
|
1659
|
+
const sample = new PluginClass();
|
|
1660
|
+
if (typeof sample.hooks === "function") return sample.hooks.call(sample, ctx, options);
|
|
1661
|
+
}
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
/**
|
|
1665
|
+
* Resolves async plugin factories and normalizes class entries into `VercubePlugin` objects.
|
|
1666
|
+
*
|
|
1667
|
+
* @param inputs - Raw `plugins` array from config, or undefined.
|
|
1668
|
+
* @returns Resolved plugins in source order (before `enforce` sorting elsewhere).
|
|
1669
|
+
* @throws {Error} If an array entry is neither `[Class]`, `[Class, options]`, nor a valid plugin shape.
|
|
1670
|
+
*/
|
|
1671
|
+
async function normalizeVercubePluginInputs(inputs) {
|
|
1672
|
+
if (!inputs?.length) return [];
|
|
1673
|
+
const out = [];
|
|
1674
|
+
for (const entry of inputs) {
|
|
1675
|
+
if (Array.isArray(entry)) {
|
|
1676
|
+
if (entry.length > 0 && isBasePluginClass(entry[0])) {
|
|
1677
|
+
out.push(vercubePluginFromClass(entry[0], entry[1]));
|
|
1678
|
+
continue;
|
|
1679
|
+
}
|
|
1680
|
+
if (entry.length === 1) {
|
|
1681
|
+
out.push(...await normalizeVercubePluginInputs([entry[0]]));
|
|
1682
|
+
continue;
|
|
1683
|
+
}
|
|
1684
|
+
throw new Error("Invalid plugin entry: use a BasePlugin class, [Class, options?], a factory function, or a plugin object");
|
|
1685
|
+
}
|
|
1686
|
+
if (isBasePluginClass(entry)) {
|
|
1687
|
+
out.push(vercubePluginFromClass(entry));
|
|
1688
|
+
continue;
|
|
1689
|
+
}
|
|
1690
|
+
const resolved = typeof entry === "function" && !isBasePluginClass(entry) ? await entry() : await Promise.resolve(entry);
|
|
1691
|
+
out.push(resolved);
|
|
1692
|
+
}
|
|
1693
|
+
return out;
|
|
1694
|
+
}
|
|
1695
|
+
/**
|
|
1696
|
+
* @param plugins - Resolved plugins in user list order.
|
|
1697
|
+
* @returns Same plugins reordered: `enforce: 'pre'`, then unspecified, then `enforce: 'post'`.
|
|
1698
|
+
*/
|
|
1699
|
+
function sortByEnforce(plugins) {
|
|
1700
|
+
const pre = [];
|
|
1701
|
+
const mid = [];
|
|
1702
|
+
const post = [];
|
|
1703
|
+
for (const p of plugins) if (p.enforce === "pre") pre.push(p);
|
|
1704
|
+
else if (p.enforce === "post") post.push(p);
|
|
1705
|
+
else mid.push(p);
|
|
1706
|
+
return [
|
|
1707
|
+
...pre,
|
|
1708
|
+
...mid,
|
|
1709
|
+
...post
|
|
1710
|
+
];
|
|
1711
|
+
}
|
|
1712
|
+
/**
|
|
1713
|
+
* Runs each plugin's `config` hook (merge order), normalizes `plugins` to resolved objects, runs `cli` hooks, and merges registered commands into `config.cli.commands`.
|
|
1714
|
+
*
|
|
1715
|
+
* @param config - Full Vercube config (may contain raw plugin inputs).
|
|
1716
|
+
* @param env - Working directory and CLI context flags.
|
|
1717
|
+
* @returns Config with `plugins` normalized and CLI commands aggregated.
|
|
1718
|
+
*/
|
|
1719
|
+
async function applyVercubePluginHooks(config, env) {
|
|
1720
|
+
const rawPlugins = config.plugins;
|
|
1721
|
+
if (!rawPlugins?.length) return config;
|
|
1722
|
+
const ordered = sortByEnforce(await normalizeVercubePluginInputs(rawPlugins));
|
|
1723
|
+
let next = {
|
|
1724
|
+
...config,
|
|
1725
|
+
plugins: ordered
|
|
1726
|
+
};
|
|
1727
|
+
for (const plugin of ordered) {
|
|
1728
|
+
const patch = await plugin.config?.(next, env);
|
|
1729
|
+
if (patch && typeof patch === "object") {
|
|
1730
|
+
const { plugins: _ignored, ...rest } = patch;
|
|
1731
|
+
const merged = defu(next, rest);
|
|
1732
|
+
merged.plugins = ordered;
|
|
1733
|
+
next = merged;
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
const commands = [...next.cli?.commands ?? []];
|
|
1737
|
+
const register = (...cmds) => {
|
|
1738
|
+
commands.push(...cmds);
|
|
1739
|
+
};
|
|
1740
|
+
const cliCtx = {
|
|
1741
|
+
cwd: env.cwd,
|
|
1742
|
+
command: env.command,
|
|
1743
|
+
config: next,
|
|
1744
|
+
register
|
|
1745
|
+
};
|
|
1746
|
+
for (const plugin of ordered) await plugin.cli?.(cliCtx);
|
|
1747
|
+
next = {
|
|
1748
|
+
...next,
|
|
1749
|
+
cli: {
|
|
1750
|
+
...next.cli,
|
|
1751
|
+
commands
|
|
1752
|
+
}
|
|
1753
|
+
};
|
|
1754
|
+
return next;
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Invokes `hooks` on each resolved plugin (dev parent process only).
|
|
1758
|
+
*
|
|
1759
|
+
* @param plugins - Normalized plugins from config (after `applyVercubePluginHooks`).
|
|
1760
|
+
* @param ctx - Hooks context from devkit (`config`, `hooks`).
|
|
1761
|
+
* @returns Resolves when every plugin `hooks` call completes.
|
|
1762
|
+
*/
|
|
1763
|
+
async function invokeVercubePluginDevHooks(plugins, ctx) {
|
|
1764
|
+
if (!plugins?.length) return;
|
|
1765
|
+
const rawHooks = ctx.hooks;
|
|
1766
|
+
if (!rawHooks || typeof rawHooks !== "object") {
|
|
1767
|
+
for (const plugin of plugins) await plugin.hooks?.(ctx);
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
const previousCleanup = pluginDevHookCleanupMap.get(rawHooks);
|
|
1771
|
+
if (previousCleanup?.length) for (const dispose of previousCleanup) dispose();
|
|
1772
|
+
const cleanup = [];
|
|
1773
|
+
const trackedHooks = new Proxy(rawHooks, { get(target, prop, receiver) {
|
|
1774
|
+
if (prop === "hook") return (name, handler, ...rest) => {
|
|
1775
|
+
const result = Reflect.apply(target.hook, target, [
|
|
1776
|
+
name,
|
|
1777
|
+
handler,
|
|
1778
|
+
...rest
|
|
1779
|
+
]);
|
|
1780
|
+
if (typeof result === "function") cleanup.push(result);
|
|
1781
|
+
else if (typeof target.removeHook === "function") cleanup.push(() => {
|
|
1782
|
+
target.removeHook(name, handler);
|
|
1783
|
+
});
|
|
1784
|
+
return result;
|
|
1785
|
+
};
|
|
1786
|
+
if (prop === "addHooks") return (hooksObject) => {
|
|
1787
|
+
const result = Reflect.apply(target.addHooks, target, [hooksObject]);
|
|
1788
|
+
if (typeof target.removeHooks === "function") cleanup.push(() => {
|
|
1789
|
+
target.removeHooks(hooksObject);
|
|
1790
|
+
});
|
|
1791
|
+
return result;
|
|
1792
|
+
};
|
|
1793
|
+
return Reflect.get(target, prop, receiver);
|
|
1794
|
+
} });
|
|
1795
|
+
const nextCtx = {
|
|
1796
|
+
...ctx,
|
|
1797
|
+
hooks: trackedHooks
|
|
1798
|
+
};
|
|
1799
|
+
for (const plugin of plugins) await plugin.hooks?.(nextCtx);
|
|
1800
|
+
pluginDevHookCleanupMap.set(rawHooks, cleanup);
|
|
1801
|
+
}
|
|
1802
|
+
//#endregion
|
|
1513
1803
|
//#region src/Config/DefaultConfig.ts
|
|
1514
1804
|
/**
|
|
1515
1805
|
* Default configuration for the Vercube application.
|
|
@@ -1595,20 +1885,33 @@ const defaultConfig = {
|
|
|
1595
1885
|
//#endregion
|
|
1596
1886
|
//#region src/Config/Loader.ts
|
|
1597
1887
|
/**
|
|
1598
|
-
* Loads
|
|
1599
|
-
*
|
|
1600
|
-
* @
|
|
1888
|
+
* Loads and merges `vercube` config from disk, applies defaults, runs env setup, then the plugin `config` and `cli` pipeline.
|
|
1889
|
+
*
|
|
1890
|
+
* @param overrides - Values merged on top of file-based config (wins over file).
|
|
1891
|
+
* @param options - `cwd`, optional `import`, and `command` for plugin context.
|
|
1892
|
+
* @returns Fully merged config with normalized `plugins` and aggregated `cli.commands`.
|
|
1601
1893
|
*/
|
|
1602
|
-
async function loadVercubeConfig(overrides) {
|
|
1894
|
+
async function loadVercubeConfig(overrides, options) {
|
|
1895
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
1603
1896
|
const config = await loadConfig({
|
|
1604
1897
|
name: "vercube",
|
|
1605
1898
|
dotenv: overrides?.c12?.dotenv ?? true,
|
|
1606
1899
|
rcFile: false,
|
|
1607
1900
|
globalRc: false,
|
|
1608
|
-
defaults: defaultConfig
|
|
1901
|
+
defaults: defaultConfig,
|
|
1902
|
+
cwd,
|
|
1903
|
+
...options?.import ? { import: options.import } : {}
|
|
1609
1904
|
});
|
|
1610
1905
|
if (config?.config?.c12?.dotenv && typeof config?.config?.c12?.dotenv === "object") await setupDotenv(config.config.c12.dotenv);
|
|
1611
|
-
|
|
1906
|
+
let merged = defu(overrides ?? {}, config?.config ?? {});
|
|
1907
|
+
const env = {
|
|
1908
|
+
cwd,
|
|
1909
|
+
dev: merged.dev,
|
|
1910
|
+
production: merged.production,
|
|
1911
|
+
command: options?.command
|
|
1912
|
+
};
|
|
1913
|
+
merged = await applyVercubePluginHooks(merged, env);
|
|
1914
|
+
return merged;
|
|
1612
1915
|
}
|
|
1613
1916
|
//#endregion
|
|
1614
1917
|
//#region src/Common/CreateApp.ts
|
|
@@ -3244,4 +3547,4 @@ let HttpStatusCode = /* @__PURE__ */ function(HttpStatusCode) {
|
|
|
3244
3547
|
return HttpStatusCode;
|
|
3245
3548
|
}({});
|
|
3246
3549
|
//#endregion
|
|
3247
|
-
export { App, BadRequestError, BaseMiddleware, BasePlugin, Body, Connect, Controller, DANGEROUS_PROPERTIES, Delete, ErrorHandlerProvider, FastResponse, ForbiddenError, Get, GlobalMiddlewareRegistry, HTTPStatus, Head, Header, Headers$1 as Headers, HooksService, HttpError, HttpServer, HttpStatusCode, InternalServerError, Listen, MetadataResolver, MethodNotAllowedError, Middleware, MultipartFormData, NotAcceptableError, NotFoundError, Options, Param, Patch, Post, Put, QueryParam, QueryParams, Redirect, Request, RequestContext, Response$1 as Response, Router, RuntimeConfig, SetHeader, StandardSchemaValidationProvider, Status, Trace, UnauthorizedError, ValidationProvider, createApp, createMetadataCtx, createMetadataMethod, defineConfig, initializeMetadata, initializeMetadataMethod, isSafeProperty, loadVercubeConfig, safeAssign, safeJsonParse, sanitizeObject, secureReviver };
|
|
3550
|
+
export { App, BadRequestError, BaseMiddleware, BasePlugin, Body, Connect, Controller, DANGEROUS_PROPERTIES, Delete, EVLOG_FINISH_KEY, EVLOG_REQUEST_LOGGER_KEY, ErrorHandlerProvider, EvlogMiddleware, FastResponse, ForbiddenError, Get, GlobalMiddlewareRegistry, HTTPStatus, Head, Header, Headers$1 as Headers, HooksService, HttpError, HttpServer, HttpStatusCode, InternalServerError, Listen, MetadataResolver, MethodNotAllowedError, Middleware, MultipartFormData, NotAcceptableError, NotFoundError, Options, Param, Patch, Post, Put, QueryParam, QueryParams, Redirect, Request, RequestContext, Response$1 as Response, Router, RuntimeConfig, SetHeader, StandardSchemaValidationProvider, Status, Trace, UnauthorizedError, ValidationProvider, applyVercubePluginHooks, createApp, createMetadataCtx, createMetadataMethod, defineConfig, defineVercubePlugin, initializeMetadata, initializeMetadataMethod, invokeVercubePluginDevHooks, isBasePluginClass, isSafeProperty, loadVercubeConfig, normalizeVercubePluginInputs, safeAssign, safeJsonParse, sanitizeObject, secureReviver, vercubePluginFromClass, withPluginOptions };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vercube/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
4
|
"description": "Core module for Vercube framework",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"pathe": "2.0.3",
|
|
29
29
|
"rou3": "0.8.1",
|
|
30
30
|
"srvx": "0.11.16",
|
|
31
|
-
"@vercube/
|
|
32
|
-
"@vercube/
|
|
31
|
+
"@vercube/logger": "1.0.0-beta.1",
|
|
32
|
+
"@vercube/di": "1.0.0-beta.1"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"rolldown": "1.
|
|
35
|
+
"rolldown": "1.1.1",
|
|
36
36
|
"zod": "4.4.3"
|
|
37
37
|
},
|
|
38
38
|
"publishConfig": {
|