@spfn/core 0.2.0-beta.13 → 0.2.0-beta.15
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/config/index.d.ts +6 -6
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/env/loader.d.ts +27 -19
- package/dist/env/loader.js +33 -25
- package/dist/env/loader.js.map +1 -1
- package/dist/nextjs/index.d.ts +2 -2
- package/dist/nextjs/index.js +1 -1
- package/dist/nextjs/index.js.map +1 -1
- package/dist/nextjs/server.d.ts +2 -2
- package/dist/nextjs/server.js +1 -1
- package/dist/nextjs/server.js.map +1 -1
- package/dist/server/index.d.ts +6 -9
- package/dist/server/index.js +191 -140
- package/dist/server/index.js.map +1 -1
- package/dist/{types-DP8nzQcY.d.ts → types-7Mhoxnnt.d.ts} +1 -1
- package/docs/env.md +43 -21
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { env } from '@spfn/core/config';
|
|
2
|
-
import {
|
|
3
|
-
import { existsSync } from 'fs';
|
|
2
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
3
|
import { resolve, join } from 'path';
|
|
4
|
+
import { parse } from 'dotenv';
|
|
5
|
+
import { logger } from '@spfn/core/logger';
|
|
5
6
|
import { Hono } from 'hono';
|
|
6
7
|
import { cors } from 'hono/cors';
|
|
7
8
|
import { registerRoutes } from '@spfn/core/route';
|
|
8
9
|
import { ErrorHandler, RequestLogger } from '@spfn/core/middleware';
|
|
9
10
|
import { streamSSE } from 'hono/streaming';
|
|
10
|
-
import { logger } from '@spfn/core/logger';
|
|
11
11
|
import { randomBytes } from 'crypto';
|
|
12
12
|
import { initDatabase, getDatabase, closeDatabase } from '@spfn/core/db';
|
|
13
13
|
import { initCache, getCache, closeCache } from '@spfn/core/cache';
|
|
@@ -315,30 +315,81 @@ var init_formatters = __esm({
|
|
|
315
315
|
};
|
|
316
316
|
}
|
|
317
317
|
});
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
318
|
+
var envLogger = logger.child("@spfn/core:env-loader");
|
|
319
|
+
function getEnvFiles(nodeEnv, server) {
|
|
320
|
+
const files = [
|
|
321
|
+
".env",
|
|
322
|
+
`.env.${nodeEnv}`
|
|
323
|
+
];
|
|
324
|
+
if (nodeEnv !== "test") {
|
|
325
|
+
files.push(".env.local");
|
|
326
|
+
}
|
|
327
|
+
files.push(`.env.${nodeEnv}.local`);
|
|
328
|
+
if (server) {
|
|
329
|
+
files.push(".env.server");
|
|
330
|
+
files.push(".env.server.local");
|
|
331
|
+
}
|
|
332
|
+
return files;
|
|
333
|
+
}
|
|
334
|
+
function parseEnvFile(filePath) {
|
|
335
|
+
if (!existsSync(filePath)) {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
return parse(readFileSync(filePath, "utf-8"));
|
|
339
|
+
}
|
|
340
|
+
function loadEnv(options = {}) {
|
|
341
|
+
const {
|
|
342
|
+
cwd = process.cwd(),
|
|
343
|
+
nodeEnv = process.env.NODE_ENV || "local",
|
|
344
|
+
server = true,
|
|
345
|
+
debug = false,
|
|
346
|
+
override = false
|
|
347
|
+
} = options;
|
|
348
|
+
const envFiles = getEnvFiles(nodeEnv, server);
|
|
349
|
+
const loadedFiles = [];
|
|
350
|
+
const existingKeys = new Set(Object.keys(process.env));
|
|
351
|
+
const merged = {};
|
|
352
|
+
for (const fileName of envFiles) {
|
|
353
|
+
const filePath = resolve(cwd, fileName);
|
|
354
|
+
const parsed = parseEnvFile(filePath);
|
|
355
|
+
if (parsed === null) {
|
|
356
|
+
continue;
|
|
333
357
|
}
|
|
358
|
+
loadedFiles.push(fileName);
|
|
359
|
+
Object.assign(merged, parsed);
|
|
360
|
+
}
|
|
361
|
+
const loadedKeys = [];
|
|
362
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
363
|
+
if (!override && existingKeys.has(key)) {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
process.env[key] = value;
|
|
367
|
+
loadedKeys.push(key);
|
|
368
|
+
}
|
|
369
|
+
if (debug && loadedFiles.length > 0) {
|
|
370
|
+
envLogger.debug(`Loaded env files: ${loadedFiles.join(", ")}`);
|
|
371
|
+
envLogger.debug(`Loaded ${loadedKeys.length} environment variables`);
|
|
372
|
+
}
|
|
373
|
+
return { loadedFiles, loadedKeys };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// src/server/dotenv-loader.ts
|
|
377
|
+
var warned = false;
|
|
378
|
+
function loadEnvFiles() {
|
|
379
|
+
if (!warned) {
|
|
380
|
+
warned = true;
|
|
381
|
+
console.warn(
|
|
382
|
+
'[SPFN] loadEnvFiles() is deprecated. Use loadEnv() from "@spfn/core/env/loader" instead.'
|
|
383
|
+
);
|
|
334
384
|
}
|
|
385
|
+
loadEnv();
|
|
335
386
|
}
|
|
336
387
|
var sseLogger = logger.child("@spfn/core:sse");
|
|
337
|
-
function createSSEHandler(router,
|
|
388
|
+
function createSSEHandler(router, config = {}, tokenManager) {
|
|
338
389
|
const {
|
|
339
390
|
pingInterval = 3e4,
|
|
340
391
|
auth: authConfig
|
|
341
|
-
} =
|
|
392
|
+
} = config;
|
|
342
393
|
return async (c) => {
|
|
343
394
|
const subject = await authenticateToken(c, tokenManager);
|
|
344
395
|
if (subject === false) {
|
|
@@ -489,10 +540,10 @@ var SSETokenManager = class {
|
|
|
489
540
|
store;
|
|
490
541
|
ttl;
|
|
491
542
|
cleanupTimer = null;
|
|
492
|
-
constructor(
|
|
493
|
-
this.ttl =
|
|
494
|
-
this.store =
|
|
495
|
-
const cleanupInterval =
|
|
543
|
+
constructor(config) {
|
|
544
|
+
this.ttl = config?.ttl ?? 3e4;
|
|
545
|
+
this.store = config?.store ?? new InMemoryTokenStore();
|
|
546
|
+
const cleanupInterval = config?.cleanupInterval ?? 6e4;
|
|
496
547
|
this.cleanupTimer = setInterval(() => void this.store.cleanup(), cleanupInterval);
|
|
497
548
|
this.cleanupTimer.unref();
|
|
498
549
|
}
|
|
@@ -587,34 +638,34 @@ function applyServerTimeouts(server, timeouts) {
|
|
|
587
638
|
server.headersTimeout = timeouts.headers;
|
|
588
639
|
}
|
|
589
640
|
}
|
|
590
|
-
function getTimeoutConfig(
|
|
641
|
+
function getTimeoutConfig(config) {
|
|
591
642
|
return {
|
|
592
|
-
request:
|
|
593
|
-
keepAlive:
|
|
594
|
-
headers:
|
|
643
|
+
request: config?.request ?? env.SERVER_TIMEOUT,
|
|
644
|
+
keepAlive: config?.keepAlive ?? env.SERVER_KEEPALIVE_TIMEOUT,
|
|
645
|
+
headers: config?.headers ?? env.SERVER_HEADERS_TIMEOUT
|
|
595
646
|
};
|
|
596
647
|
}
|
|
597
|
-
function getShutdownTimeout(
|
|
598
|
-
return
|
|
648
|
+
function getShutdownTimeout(config) {
|
|
649
|
+
return config?.timeout ?? env.SHUTDOWN_TIMEOUT;
|
|
599
650
|
}
|
|
600
|
-
function buildMiddlewareOrder(
|
|
651
|
+
function buildMiddlewareOrder(config) {
|
|
601
652
|
const order = [];
|
|
602
|
-
const middlewareConfig =
|
|
653
|
+
const middlewareConfig = config.middleware ?? {};
|
|
603
654
|
const enableLogger = middlewareConfig.logger !== false;
|
|
604
655
|
const enableCors = middlewareConfig.cors !== false;
|
|
605
656
|
const enableErrorHandler = middlewareConfig.errorHandler !== false;
|
|
606
657
|
if (enableLogger) order.push("RequestLogger");
|
|
607
658
|
if (enableCors) order.push("CORS");
|
|
608
|
-
|
|
609
|
-
if (
|
|
659
|
+
config.use?.forEach((_, i) => order.push(`Custom[${i}]`));
|
|
660
|
+
if (config.beforeRoutes) order.push("beforeRoutes hook");
|
|
610
661
|
order.push("Routes");
|
|
611
|
-
if (
|
|
662
|
+
if (config.afterRoutes) order.push("afterRoutes hook");
|
|
612
663
|
if (enableErrorHandler) order.push("ErrorHandler");
|
|
613
664
|
return order;
|
|
614
665
|
}
|
|
615
|
-
function buildStartupConfig(
|
|
616
|
-
const middlewareConfig =
|
|
617
|
-
const healthCheckConfig =
|
|
666
|
+
function buildStartupConfig(config, timeouts) {
|
|
667
|
+
const middlewareConfig = config.middleware ?? {};
|
|
668
|
+
const healthCheckConfig = config.healthCheck ?? {};
|
|
618
669
|
const healthCheckEnabled = healthCheckConfig.enabled !== false;
|
|
619
670
|
const healthCheckPath = healthCheckConfig.path ?? "/health";
|
|
620
671
|
const healthCheckDetailed = healthCheckConfig.detailed ?? env.NODE_ENV === "development";
|
|
@@ -623,7 +674,7 @@ function buildStartupConfig(config2, timeouts) {
|
|
|
623
674
|
logger: middlewareConfig.logger !== false,
|
|
624
675
|
cors: middlewareConfig.cors !== false,
|
|
625
676
|
errorHandler: middlewareConfig.errorHandler !== false,
|
|
626
|
-
custom:
|
|
677
|
+
custom: config.use?.length ?? 0
|
|
627
678
|
},
|
|
628
679
|
healthCheck: healthCheckEnabled ? {
|
|
629
680
|
enabled: true,
|
|
@@ -631,8 +682,8 @@ function buildStartupConfig(config2, timeouts) {
|
|
|
631
682
|
detailed: healthCheckDetailed
|
|
632
683
|
} : { enabled: false },
|
|
633
684
|
hooks: {
|
|
634
|
-
beforeRoutes: !!
|
|
635
|
-
afterRoutes: !!
|
|
685
|
+
beforeRoutes: !!config.beforeRoutes,
|
|
686
|
+
afterRoutes: !!config.afterRoutes
|
|
636
687
|
},
|
|
637
688
|
timeout: {
|
|
638
689
|
request: `${timeouts.request}ms`,
|
|
@@ -640,23 +691,23 @@ function buildStartupConfig(config2, timeouts) {
|
|
|
640
691
|
headers: `${timeouts.headers}ms`
|
|
641
692
|
},
|
|
642
693
|
shutdown: {
|
|
643
|
-
timeout: `${
|
|
694
|
+
timeout: `${config.shutdown?.timeout ?? env.SHUTDOWN_TIMEOUT}ms`
|
|
644
695
|
}
|
|
645
696
|
};
|
|
646
697
|
}
|
|
647
698
|
var serverLogger = logger.child("@spfn/core:server");
|
|
648
699
|
|
|
649
700
|
// src/server/create-server.ts
|
|
650
|
-
async function createServer(
|
|
701
|
+
async function createServer(config) {
|
|
651
702
|
const cwd = process.cwd();
|
|
652
703
|
const appPath = join(cwd, "src", "server", "app.ts");
|
|
653
704
|
const appJsPath = join(cwd, "src", "server", "app");
|
|
654
705
|
if (existsSync(appPath) || existsSync(appJsPath)) {
|
|
655
|
-
return await loadCustomApp(appPath, appJsPath,
|
|
706
|
+
return await loadCustomApp(appPath, appJsPath, config);
|
|
656
707
|
}
|
|
657
|
-
return await createAutoConfiguredApp(
|
|
708
|
+
return await createAutoConfiguredApp(config);
|
|
658
709
|
}
|
|
659
|
-
async function loadCustomApp(appPath, appJsPath,
|
|
710
|
+
async function loadCustomApp(appPath, appJsPath, config) {
|
|
660
711
|
const actualPath = existsSync(appPath) ? appPath : appJsPath;
|
|
661
712
|
const appModule = await import(actualPath);
|
|
662
713
|
const appFactory = appModule.default;
|
|
@@ -664,15 +715,15 @@ async function loadCustomApp(appPath, appJsPath, config2) {
|
|
|
664
715
|
throw new Error("app.ts must export a default function that returns a Hono app");
|
|
665
716
|
}
|
|
666
717
|
const app = await appFactory();
|
|
667
|
-
if (
|
|
668
|
-
const routes = registerRoutes(app,
|
|
669
|
-
logRegisteredRoutes(routes,
|
|
718
|
+
if (config?.routes) {
|
|
719
|
+
const routes = registerRoutes(app, config.routes, config.middlewares);
|
|
720
|
+
logRegisteredRoutes(routes, config?.debug ?? false);
|
|
670
721
|
}
|
|
671
722
|
return app;
|
|
672
723
|
}
|
|
673
|
-
async function createAutoConfiguredApp(
|
|
724
|
+
async function createAutoConfiguredApp(config) {
|
|
674
725
|
const app = new Hono();
|
|
675
|
-
const middlewareConfig =
|
|
726
|
+
const middlewareConfig = config?.middleware ?? {};
|
|
676
727
|
const enableLogger = middlewareConfig.logger !== false;
|
|
677
728
|
const enableCors = middlewareConfig.cors !== false;
|
|
678
729
|
const enableErrorHandler = middlewareConfig.errorHandler !== false;
|
|
@@ -682,31 +733,31 @@ async function createAutoConfiguredApp(config2) {
|
|
|
682
733
|
await next();
|
|
683
734
|
});
|
|
684
735
|
}
|
|
685
|
-
applyDefaultMiddleware(app,
|
|
686
|
-
if (Array.isArray(
|
|
687
|
-
|
|
736
|
+
applyDefaultMiddleware(app, config, enableLogger, enableCors);
|
|
737
|
+
if (Array.isArray(config?.use)) {
|
|
738
|
+
config.use.forEach((mw) => app.use("*", mw));
|
|
688
739
|
}
|
|
689
|
-
registerHealthCheckEndpoint(app,
|
|
690
|
-
await executeBeforeRoutesHook(app,
|
|
691
|
-
await loadAppRoutes(app,
|
|
692
|
-
registerSSEEndpoint(app,
|
|
693
|
-
await executeAfterRoutesHook(app,
|
|
740
|
+
registerHealthCheckEndpoint(app, config);
|
|
741
|
+
await executeBeforeRoutesHook(app, config);
|
|
742
|
+
await loadAppRoutes(app, config);
|
|
743
|
+
registerSSEEndpoint(app, config);
|
|
744
|
+
await executeAfterRoutesHook(app, config);
|
|
694
745
|
if (enableErrorHandler) {
|
|
695
746
|
app.onError(ErrorHandler());
|
|
696
747
|
}
|
|
697
748
|
return app;
|
|
698
749
|
}
|
|
699
|
-
function applyDefaultMiddleware(app,
|
|
750
|
+
function applyDefaultMiddleware(app, config, enableLogger, enableCors) {
|
|
700
751
|
if (enableLogger) {
|
|
701
752
|
app.use("*", RequestLogger());
|
|
702
753
|
}
|
|
703
754
|
if (enableCors) {
|
|
704
|
-
const corsOptions =
|
|
755
|
+
const corsOptions = config?.cors !== false ? config?.cors : void 0;
|
|
705
756
|
app.use("*", cors(corsOptions));
|
|
706
757
|
}
|
|
707
758
|
}
|
|
708
|
-
function registerHealthCheckEndpoint(app,
|
|
709
|
-
const healthCheckConfig =
|
|
759
|
+
function registerHealthCheckEndpoint(app, config) {
|
|
760
|
+
const healthCheckConfig = config?.healthCheck ?? {};
|
|
710
761
|
const healthCheckEnabled = healthCheckConfig.enabled !== false;
|
|
711
762
|
const healthCheckPath = healthCheckConfig.path ?? "/health";
|
|
712
763
|
const healthCheckDetailed = healthCheckConfig.detailed ?? process.env.NODE_ENV === "development";
|
|
@@ -715,15 +766,15 @@ function registerHealthCheckEndpoint(app, config2) {
|
|
|
715
766
|
serverLogger.debug(`Health check endpoint enabled at ${healthCheckPath}`);
|
|
716
767
|
}
|
|
717
768
|
}
|
|
718
|
-
async function executeBeforeRoutesHook(app,
|
|
719
|
-
if (
|
|
720
|
-
await
|
|
769
|
+
async function executeBeforeRoutesHook(app, config) {
|
|
770
|
+
if (config?.lifecycle?.beforeRoutes) {
|
|
771
|
+
await config.lifecycle.beforeRoutes(app);
|
|
721
772
|
}
|
|
722
773
|
}
|
|
723
|
-
async function loadAppRoutes(app,
|
|
724
|
-
const debug = isDebugMode(
|
|
725
|
-
if (
|
|
726
|
-
const routes = registerRoutes(app,
|
|
774
|
+
async function loadAppRoutes(app, config) {
|
|
775
|
+
const debug = isDebugMode(config);
|
|
776
|
+
if (config?.routes) {
|
|
777
|
+
const routes = registerRoutes(app, config.routes, config.middlewares);
|
|
727
778
|
logRegisteredRoutes(routes, debug);
|
|
728
779
|
} else if (debug) {
|
|
729
780
|
serverLogger.warn("\u26A0\uFE0F No routes configured. Use defineServerConfig().routes() to register routes.");
|
|
@@ -744,19 +795,19 @@ function logRegisteredRoutes(routes, debug) {
|
|
|
744
795
|
serverLogger.info(`\u2713 Routes registered (${routes.length}):
|
|
745
796
|
${routeLines}`);
|
|
746
797
|
}
|
|
747
|
-
async function executeAfterRoutesHook(app,
|
|
748
|
-
if (
|
|
749
|
-
await
|
|
798
|
+
async function executeAfterRoutesHook(app, config) {
|
|
799
|
+
if (config?.lifecycle?.afterRoutes) {
|
|
800
|
+
await config.lifecycle.afterRoutes(app);
|
|
750
801
|
}
|
|
751
802
|
}
|
|
752
|
-
function registerSSEEndpoint(app,
|
|
753
|
-
if (!
|
|
803
|
+
function registerSSEEndpoint(app, config) {
|
|
804
|
+
if (!config?.events) {
|
|
754
805
|
return;
|
|
755
806
|
}
|
|
756
|
-
const eventsConfig =
|
|
807
|
+
const eventsConfig = config.eventsConfig ?? {};
|
|
757
808
|
const streamPath = eventsConfig.path ?? "/events/stream";
|
|
758
809
|
const authConfig = eventsConfig.auth;
|
|
759
|
-
const debug = isDebugMode(
|
|
810
|
+
const debug = isDebugMode(config);
|
|
760
811
|
let tokenManager;
|
|
761
812
|
if (authConfig?.enabled) {
|
|
762
813
|
tokenManager = new SSETokenManager({
|
|
@@ -764,7 +815,7 @@ function registerSSEEndpoint(app, config2) {
|
|
|
764
815
|
store: authConfig.store
|
|
765
816
|
});
|
|
766
817
|
const tokenPath = streamPath.replace(/\/[^/]+$/, "/token");
|
|
767
|
-
const mwHandlers = (
|
|
818
|
+
const mwHandlers = (config.middlewares ?? []).map((mw) => mw.handler);
|
|
768
819
|
const getSubject = authConfig.getSubject ?? ((c) => c.get("auth")?.userId ?? null);
|
|
769
820
|
app.post(tokenPath, ...mwHandlers, async (c) => {
|
|
770
821
|
const subject = getSubject(c);
|
|
@@ -778,17 +829,17 @@ function registerSSEEndpoint(app, config2) {
|
|
|
778
829
|
serverLogger.info(`\u2713 SSE token endpoint registered at POST ${tokenPath}`);
|
|
779
830
|
}
|
|
780
831
|
}
|
|
781
|
-
app.get(streamPath, createSSEHandler(
|
|
832
|
+
app.get(streamPath, createSSEHandler(config.events, eventsConfig, tokenManager));
|
|
782
833
|
if (debug) {
|
|
783
|
-
const eventNames =
|
|
834
|
+
const eventNames = config.events.eventNames;
|
|
784
835
|
serverLogger.info(`\u2713 SSE endpoint registered at ${streamPath}`, {
|
|
785
836
|
events: eventNames,
|
|
786
837
|
auth: !!authConfig?.enabled
|
|
787
838
|
});
|
|
788
839
|
}
|
|
789
840
|
}
|
|
790
|
-
function isDebugMode(
|
|
791
|
-
return
|
|
841
|
+
function isDebugMode(config) {
|
|
842
|
+
return config?.debug ?? process.env.NODE_ENV === "development";
|
|
792
843
|
}
|
|
793
844
|
var jobLogger = logger.child("@spfn/core:job");
|
|
794
845
|
var bossInstance = null;
|
|
@@ -1024,16 +1075,16 @@ function printBanner(options) {
|
|
|
1024
1075
|
}
|
|
1025
1076
|
|
|
1026
1077
|
// src/server/validation.ts
|
|
1027
|
-
function validateServerConfig(
|
|
1028
|
-
if (
|
|
1029
|
-
if (!Number.isInteger(
|
|
1078
|
+
function validateServerConfig(config) {
|
|
1079
|
+
if (config.port !== void 0) {
|
|
1080
|
+
if (!Number.isInteger(config.port) || config.port < 0 || config.port > 65535) {
|
|
1030
1081
|
throw new Error(
|
|
1031
|
-
`Invalid port: ${
|
|
1082
|
+
`Invalid port: ${config.port}. Port must be an integer between 0 and 65535.`
|
|
1032
1083
|
);
|
|
1033
1084
|
}
|
|
1034
1085
|
}
|
|
1035
|
-
if (
|
|
1036
|
-
const { request, keepAlive, headers } =
|
|
1086
|
+
if (config.timeout) {
|
|
1087
|
+
const { request, keepAlive, headers } = config.timeout;
|
|
1037
1088
|
if (request !== void 0 && (request < 0 || !Number.isFinite(request))) {
|
|
1038
1089
|
throw new Error(`Invalid timeout.request: ${request}. Must be a positive number.`);
|
|
1039
1090
|
}
|
|
@@ -1049,16 +1100,16 @@ function validateServerConfig(config2) {
|
|
|
1049
1100
|
);
|
|
1050
1101
|
}
|
|
1051
1102
|
}
|
|
1052
|
-
if (
|
|
1053
|
-
const timeout =
|
|
1103
|
+
if (config.shutdown?.timeout !== void 0) {
|
|
1104
|
+
const timeout = config.shutdown.timeout;
|
|
1054
1105
|
if (timeout < 0 || !Number.isFinite(timeout)) {
|
|
1055
1106
|
throw new Error(`Invalid shutdown.timeout: ${timeout}. Must be a positive number.`);
|
|
1056
1107
|
}
|
|
1057
1108
|
}
|
|
1058
|
-
if (
|
|
1059
|
-
if (!
|
|
1109
|
+
if (config.healthCheck?.path) {
|
|
1110
|
+
if (!config.healthCheck.path.startsWith("/")) {
|
|
1060
1111
|
throw new Error(
|
|
1061
|
-
`Invalid healthCheck.path: "${
|
|
1112
|
+
`Invalid healthCheck.path: "${config.healthCheck.path}". Must start with "/".`
|
|
1062
1113
|
);
|
|
1063
1114
|
}
|
|
1064
1115
|
}
|
|
@@ -1075,9 +1126,9 @@ var CONFIG_FILE_PATHS = [
|
|
|
1075
1126
|
"src/server/server.config.ts"
|
|
1076
1127
|
];
|
|
1077
1128
|
var processHandlersRegistered = false;
|
|
1078
|
-
async function startServer(
|
|
1079
|
-
|
|
1080
|
-
const finalConfig = await loadAndMergeConfig(
|
|
1129
|
+
async function startServer(config) {
|
|
1130
|
+
loadEnv();
|
|
1131
|
+
const finalConfig = await loadAndMergeConfig(config);
|
|
1081
1132
|
const { host, port, debug } = finalConfig;
|
|
1082
1133
|
validateServerConfig(finalConfig);
|
|
1083
1134
|
if (!host || !port) {
|
|
@@ -1135,7 +1186,7 @@ async function startServer(config2) {
|
|
|
1135
1186
|
throw error;
|
|
1136
1187
|
}
|
|
1137
1188
|
}
|
|
1138
|
-
async function loadAndMergeConfig(
|
|
1189
|
+
async function loadAndMergeConfig(config) {
|
|
1139
1190
|
const cwd = process.cwd();
|
|
1140
1191
|
let fileConfig = {};
|
|
1141
1192
|
let loadedConfigPath = null;
|
|
@@ -1159,26 +1210,26 @@ async function loadAndMergeConfig(config2) {
|
|
|
1159
1210
|
}
|
|
1160
1211
|
return {
|
|
1161
1212
|
...fileConfig,
|
|
1162
|
-
...
|
|
1163
|
-
port:
|
|
1164
|
-
host:
|
|
1213
|
+
...config,
|
|
1214
|
+
port: config?.port ?? fileConfig?.port ?? env.PORT,
|
|
1215
|
+
host: config?.host ?? fileConfig?.host ?? env.HOST
|
|
1165
1216
|
};
|
|
1166
1217
|
}
|
|
1167
|
-
function getInfrastructureConfig(
|
|
1218
|
+
function getInfrastructureConfig(config) {
|
|
1168
1219
|
return {
|
|
1169
|
-
database:
|
|
1170
|
-
redis:
|
|
1220
|
+
database: config.infrastructure?.database !== false,
|
|
1221
|
+
redis: config.infrastructure?.redis !== false
|
|
1171
1222
|
};
|
|
1172
1223
|
}
|
|
1173
|
-
async function initializeInfrastructure(
|
|
1174
|
-
if (
|
|
1224
|
+
async function initializeInfrastructure(config) {
|
|
1225
|
+
if (config.lifecycle?.beforeInfrastructure) {
|
|
1175
1226
|
serverLogger.debug("Executing beforeInfrastructure hook...");
|
|
1176
|
-
await
|
|
1227
|
+
await config.lifecycle.beforeInfrastructure(config);
|
|
1177
1228
|
}
|
|
1178
|
-
const infraConfig = getInfrastructureConfig(
|
|
1229
|
+
const infraConfig = getInfrastructureConfig(config);
|
|
1179
1230
|
if (infraConfig.database) {
|
|
1180
1231
|
serverLogger.debug("Initializing database...");
|
|
1181
|
-
await initDatabase(
|
|
1232
|
+
await initDatabase(config.database);
|
|
1182
1233
|
} else {
|
|
1183
1234
|
serverLogger.debug("Database initialization disabled");
|
|
1184
1235
|
}
|
|
@@ -1188,11 +1239,11 @@ async function initializeInfrastructure(config2) {
|
|
|
1188
1239
|
} else {
|
|
1189
1240
|
serverLogger.debug("Redis initialization disabled");
|
|
1190
1241
|
}
|
|
1191
|
-
if (
|
|
1242
|
+
if (config.lifecycle?.afterInfrastructure) {
|
|
1192
1243
|
serverLogger.debug("Executing afterInfrastructure hook...");
|
|
1193
|
-
await
|
|
1244
|
+
await config.lifecycle.afterInfrastructure();
|
|
1194
1245
|
}
|
|
1195
|
-
if (
|
|
1246
|
+
if (config.jobs) {
|
|
1196
1247
|
const dbUrl = env.DATABASE_URL;
|
|
1197
1248
|
if (!dbUrl) {
|
|
1198
1249
|
throw new Error(
|
|
@@ -1202,22 +1253,22 @@ async function initializeInfrastructure(config2) {
|
|
|
1202
1253
|
serverLogger.debug("Initializing pg-boss...");
|
|
1203
1254
|
await initBoss({
|
|
1204
1255
|
connectionString: dbUrl,
|
|
1205
|
-
...
|
|
1256
|
+
...config.jobsConfig
|
|
1206
1257
|
});
|
|
1207
1258
|
serverLogger.debug("Registering jobs...");
|
|
1208
|
-
await registerJobs(
|
|
1259
|
+
await registerJobs(config.jobs);
|
|
1209
1260
|
}
|
|
1210
|
-
if (
|
|
1211
|
-
const infraConfig2 = getInfrastructureConfig(
|
|
1261
|
+
if (config.workflows) {
|
|
1262
|
+
const infraConfig2 = getInfrastructureConfig(config);
|
|
1212
1263
|
if (!infraConfig2.database) {
|
|
1213
1264
|
throw new Error(
|
|
1214
1265
|
"Workflows require database connection. Ensure database is enabled in infrastructure config."
|
|
1215
1266
|
);
|
|
1216
1267
|
}
|
|
1217
1268
|
serverLogger.debug("Initializing workflow engine...");
|
|
1218
|
-
|
|
1269
|
+
config.workflows._init(
|
|
1219
1270
|
getDatabase(),
|
|
1220
|
-
|
|
1271
|
+
config.workflowsConfig
|
|
1221
1272
|
);
|
|
1222
1273
|
serverLogger.info("Workflow engine initialized");
|
|
1223
1274
|
}
|
|
@@ -1230,8 +1281,8 @@ function startHttpServer(app, host, port) {
|
|
|
1230
1281
|
hostname: host
|
|
1231
1282
|
});
|
|
1232
1283
|
}
|
|
1233
|
-
function logMiddlewareOrder(
|
|
1234
|
-
const middlewareOrder = buildMiddlewareOrder(
|
|
1284
|
+
function logMiddlewareOrder(config) {
|
|
1285
|
+
const middlewareOrder = buildMiddlewareOrder(config);
|
|
1235
1286
|
serverLogger.debug("Middleware execution order", {
|
|
1236
1287
|
order: middlewareOrder
|
|
1237
1288
|
});
|
|
@@ -1243,8 +1294,8 @@ function logServerTimeouts(timeouts) {
|
|
|
1243
1294
|
headers: `${timeouts.headers}ms`
|
|
1244
1295
|
});
|
|
1245
1296
|
}
|
|
1246
|
-
function logServerStarted(debug, host, port,
|
|
1247
|
-
const startupConfig = buildStartupConfig(
|
|
1297
|
+
function logServerStarted(debug, host, port, config, timeouts) {
|
|
1298
|
+
const startupConfig = buildStartupConfig(config, timeouts);
|
|
1248
1299
|
serverLogger.info("Server started successfully", {
|
|
1249
1300
|
mode: debug ? "development" : "production",
|
|
1250
1301
|
host,
|
|
@@ -1252,7 +1303,7 @@ function logServerStarted(debug, host, port, config2, timeouts) {
|
|
|
1252
1303
|
config: startupConfig
|
|
1253
1304
|
});
|
|
1254
1305
|
}
|
|
1255
|
-
function createShutdownHandler(server,
|
|
1306
|
+
function createShutdownHandler(server, config, shutdownState) {
|
|
1256
1307
|
return async () => {
|
|
1257
1308
|
if (shutdownState.isShuttingDown) {
|
|
1258
1309
|
serverLogger.debug("Shutdown already in progress for this instance, skipping");
|
|
@@ -1283,7 +1334,7 @@ function createShutdownHandler(server, config2, shutdownState) {
|
|
|
1283
1334
|
if (timeoutId) clearTimeout(timeoutId);
|
|
1284
1335
|
serverLogger.warn("HTTP server close timeout, forcing shutdown", error);
|
|
1285
1336
|
});
|
|
1286
|
-
if (
|
|
1337
|
+
if (config.jobs) {
|
|
1287
1338
|
serverLogger.debug("Stopping pg-boss...");
|
|
1288
1339
|
try {
|
|
1289
1340
|
await stopBoss();
|
|
@@ -1291,15 +1342,15 @@ function createShutdownHandler(server, config2, shutdownState) {
|
|
|
1291
1342
|
serverLogger.error("pg-boss stop failed", error);
|
|
1292
1343
|
}
|
|
1293
1344
|
}
|
|
1294
|
-
if (
|
|
1345
|
+
if (config.lifecycle?.beforeShutdown) {
|
|
1295
1346
|
serverLogger.debug("Executing beforeShutdown hook...");
|
|
1296
1347
|
try {
|
|
1297
|
-
await
|
|
1348
|
+
await config.lifecycle.beforeShutdown();
|
|
1298
1349
|
} catch (error) {
|
|
1299
1350
|
serverLogger.error("beforeShutdown hook failed", error);
|
|
1300
1351
|
}
|
|
1301
1352
|
}
|
|
1302
|
-
const infraConfig = getInfrastructureConfig(
|
|
1353
|
+
const infraConfig = getInfrastructureConfig(config);
|
|
1303
1354
|
if (infraConfig.database) {
|
|
1304
1355
|
serverLogger.debug("Closing database connections...");
|
|
1305
1356
|
await closeInfrastructure(closeDatabase, "Database", TIMEOUTS.DATABASE_CLOSE);
|
|
@@ -1330,14 +1381,14 @@ async function closeInfrastructure(closeFn, name, timeout) {
|
|
|
1330
1381
|
serverLogger.error(`${name} close failed or timed out`, error);
|
|
1331
1382
|
}
|
|
1332
1383
|
}
|
|
1333
|
-
function createGracefulShutdown(shutdownServer,
|
|
1384
|
+
function createGracefulShutdown(shutdownServer, config, shutdownState) {
|
|
1334
1385
|
return async (signal) => {
|
|
1335
1386
|
if (shutdownState.isShuttingDown) {
|
|
1336
1387
|
serverLogger.warn(`${signal} received but shutdown already in progress, ignoring`);
|
|
1337
1388
|
return;
|
|
1338
1389
|
}
|
|
1339
1390
|
serverLogger.info(`${signal} received, starting graceful shutdown...`);
|
|
1340
|
-
const shutdownTimeout = getShutdownTimeout(
|
|
1391
|
+
const shutdownTimeout = getShutdownTimeout(config.shutdown);
|
|
1341
1392
|
let timeoutId;
|
|
1342
1393
|
try {
|
|
1343
1394
|
await Promise.race([
|
|
@@ -1423,10 +1474,10 @@ function registerProcessHandlers(shutdown) {
|
|
|
1423
1474
|
});
|
|
1424
1475
|
serverLogger.debug("Process-level shutdown handlers registered successfully");
|
|
1425
1476
|
}
|
|
1426
|
-
async function cleanupOnFailure(
|
|
1477
|
+
async function cleanupOnFailure(config) {
|
|
1427
1478
|
try {
|
|
1428
1479
|
serverLogger.debug("Cleaning up after initialization failure...");
|
|
1429
|
-
const infraConfig = getInfrastructureConfig(
|
|
1480
|
+
const infraConfig = getInfrastructureConfig(config);
|
|
1430
1481
|
if (infraConfig.database) {
|
|
1431
1482
|
await closeInfrastructure(closeDatabase, "Database", TIMEOUTS.DATABASE_CLOSE);
|
|
1432
1483
|
}
|
|
@@ -1558,10 +1609,10 @@ var ServerConfigBuilder = class {
|
|
|
1558
1609
|
* .build();
|
|
1559
1610
|
* ```
|
|
1560
1611
|
*/
|
|
1561
|
-
jobs(router,
|
|
1612
|
+
jobs(router, config) {
|
|
1562
1613
|
this.config.jobs = router;
|
|
1563
|
-
if (
|
|
1564
|
-
this.config.jobsConfig =
|
|
1614
|
+
if (config) {
|
|
1615
|
+
this.config.jobsConfig = config;
|
|
1565
1616
|
}
|
|
1566
1617
|
return this;
|
|
1567
1618
|
}
|
|
@@ -1593,10 +1644,10 @@ var ServerConfigBuilder = class {
|
|
|
1593
1644
|
* .events(eventRouter, { path: '/sse' })
|
|
1594
1645
|
* ```
|
|
1595
1646
|
*/
|
|
1596
|
-
events(router,
|
|
1647
|
+
events(router, config) {
|
|
1597
1648
|
this.config.events = router;
|
|
1598
|
-
if (
|
|
1599
|
-
this.config.eventsConfig =
|
|
1649
|
+
if (config) {
|
|
1650
|
+
this.config.eventsConfig = config;
|
|
1600
1651
|
}
|
|
1601
1652
|
return this;
|
|
1602
1653
|
}
|
|
@@ -1662,10 +1713,10 @@ var ServerConfigBuilder = class {
|
|
|
1662
1713
|
* .build();
|
|
1663
1714
|
* ```
|
|
1664
1715
|
*/
|
|
1665
|
-
workflows(router,
|
|
1716
|
+
workflows(router, config) {
|
|
1666
1717
|
this.config.workflows = router;
|
|
1667
|
-
if (
|
|
1668
|
-
this.config.workflowsConfig =
|
|
1718
|
+
if (config) {
|
|
1719
|
+
this.config.workflowsConfig = config;
|
|
1669
1720
|
}
|
|
1670
1721
|
return this;
|
|
1671
1722
|
}
|
|
@@ -1716,6 +1767,6 @@ function defineServerConfig() {
|
|
|
1716
1767
|
return new ServerConfigBuilder();
|
|
1717
1768
|
}
|
|
1718
1769
|
|
|
1719
|
-
export { createServer, defineServerConfig, loadEnvFiles, startServer };
|
|
1770
|
+
export { createServer, defineServerConfig, loadEnv, loadEnvFiles, startServer };
|
|
1720
1771
|
//# sourceMappingURL=index.js.map
|
|
1721
1772
|
//# sourceMappingURL=index.js.map
|