@spfn/core 0.1.0-alpha.84 → 0.1.0-alpha.86
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/cache/index.js +12 -1
- package/dist/cache/index.js.map +1 -1
- package/dist/client/index.d.ts +192 -46
- package/dist/client/index.js +181 -9
- package/dist/client/index.js.map +1 -1
- package/dist/client/nextjs/index.d.ts +557 -0
- package/dist/client/nextjs/index.js +371 -0
- package/dist/client/nextjs/index.js.map +1 -0
- package/dist/codegen/generators/index.js +12 -1
- package/dist/codegen/generators/index.js.map +1 -1
- package/dist/codegen/index.js +12 -1
- package/dist/codegen/index.js.map +1 -1
- package/dist/db/index.js +12 -1
- package/dist/db/index.js.map +1 -1
- package/dist/env/index.js +12 -1
- package/dist/env/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +148 -13
- package/dist/index.js.map +1 -1
- package/dist/logger/index.js +12 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/middleware/index.js +12 -1
- package/dist/middleware/index.js.map +1 -1
- package/dist/route/index.js +15 -3
- package/dist/route/index.js.map +1 -1
- package/dist/server/index.d.ts +53 -2
- package/dist/server/index.js +148 -13
- package/dist/server/index.js.map +1 -1
- package/package.json +7 -1
package/dist/index.js
CHANGED
|
@@ -104,7 +104,18 @@ function formatConsole(metadata, colorize = true) {
|
|
|
104
104
|
}
|
|
105
105
|
if (metadata.context && Object.keys(metadata.context).length > 0) {
|
|
106
106
|
Object.entries(metadata.context).forEach(([key, value]) => {
|
|
107
|
-
|
|
107
|
+
let valueStr;
|
|
108
|
+
if (typeof value === "string") {
|
|
109
|
+
valueStr = value;
|
|
110
|
+
} else if (typeof value === "object" && value !== null) {
|
|
111
|
+
try {
|
|
112
|
+
valueStr = JSON.stringify(value);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
valueStr = "[circular]";
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
valueStr = String(value);
|
|
118
|
+
}
|
|
108
119
|
if (colorize) {
|
|
109
120
|
parts.push(`${COLORS.dim}[${key}=${valueStr}]${COLORS.reset}`);
|
|
110
121
|
} else {
|
|
@@ -2851,6 +2862,7 @@ async function loadRoutes(app, options) {
|
|
|
2851
2862
|
|
|
2852
2863
|
// src/route/bind.ts
|
|
2853
2864
|
init_errors();
|
|
2865
|
+
init_logger2();
|
|
2854
2866
|
|
|
2855
2867
|
// src/middleware/error-handler.ts
|
|
2856
2868
|
init_logger2();
|
|
@@ -3116,29 +3128,133 @@ function buildStartupConfig(config, timeouts) {
|
|
|
3116
3128
|
};
|
|
3117
3129
|
}
|
|
3118
3130
|
|
|
3131
|
+
// src/server/plugin-discovery.ts
|
|
3132
|
+
init_logger2();
|
|
3133
|
+
var pluginLogger = logger.child("plugin");
|
|
3134
|
+
async function discoverPlugins(cwd = process.cwd()) {
|
|
3135
|
+
const plugins = [];
|
|
3136
|
+
const nodeModulesPath = join(cwd, "node_modules");
|
|
3137
|
+
try {
|
|
3138
|
+
const projectPkgPath = join(cwd, "package.json");
|
|
3139
|
+
if (!existsSync(projectPkgPath)) {
|
|
3140
|
+
pluginLogger.debug("No package.json found, skipping plugin discovery");
|
|
3141
|
+
return plugins;
|
|
3142
|
+
}
|
|
3143
|
+
const projectPkg = JSON.parse(readFileSync(projectPkgPath, "utf-8"));
|
|
3144
|
+
const dependencies = {
|
|
3145
|
+
...projectPkg.dependencies,
|
|
3146
|
+
...projectPkg.devDependencies
|
|
3147
|
+
};
|
|
3148
|
+
for (const [packageName] of Object.entries(dependencies)) {
|
|
3149
|
+
if (!packageName.startsWith("@spfn/")) {
|
|
3150
|
+
continue;
|
|
3151
|
+
}
|
|
3152
|
+
try {
|
|
3153
|
+
const plugin = await loadPluginFromPackage(packageName, nodeModulesPath);
|
|
3154
|
+
if (plugin) {
|
|
3155
|
+
plugins.push(plugin);
|
|
3156
|
+
pluginLogger.info("Plugin discovered", {
|
|
3157
|
+
name: plugin.name,
|
|
3158
|
+
hooks: getPluginHookNames(plugin)
|
|
3159
|
+
});
|
|
3160
|
+
}
|
|
3161
|
+
} catch (error) {
|
|
3162
|
+
pluginLogger.debug("Failed to load plugin", {
|
|
3163
|
+
package: packageName,
|
|
3164
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
3165
|
+
});
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
} catch (error) {
|
|
3169
|
+
pluginLogger.warn("Plugin discovery failed", {
|
|
3170
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
3171
|
+
});
|
|
3172
|
+
}
|
|
3173
|
+
return plugins;
|
|
3174
|
+
}
|
|
3175
|
+
async function loadPluginFromPackage(packageName, nodeModulesPath) {
|
|
3176
|
+
const pkgPath = join(nodeModulesPath, ...packageName.split("/"), "package.json");
|
|
3177
|
+
if (!existsSync(pkgPath)) {
|
|
3178
|
+
return null;
|
|
3179
|
+
}
|
|
3180
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
3181
|
+
const packageDir = dirname(pkgPath);
|
|
3182
|
+
const mainEntry = pkg.main || "dist/index.js";
|
|
3183
|
+
const mainPath = join(packageDir, mainEntry);
|
|
3184
|
+
if (!existsSync(mainPath)) {
|
|
3185
|
+
return null;
|
|
3186
|
+
}
|
|
3187
|
+
try {
|
|
3188
|
+
const module = await import(mainPath);
|
|
3189
|
+
if (module.spfnPlugin && isValidPlugin(module.spfnPlugin)) {
|
|
3190
|
+
return module.spfnPlugin;
|
|
3191
|
+
}
|
|
3192
|
+
return null;
|
|
3193
|
+
} catch (error) {
|
|
3194
|
+
return null;
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
function isValidPlugin(plugin) {
|
|
3198
|
+
return plugin && typeof plugin === "object" && typeof plugin.name === "string" && (typeof plugin.afterInfrastructure === "function" || typeof plugin.beforeRoutes === "function" || typeof plugin.afterRoutes === "function" || typeof plugin.afterStart === "function" || typeof plugin.beforeShutdown === "function");
|
|
3199
|
+
}
|
|
3200
|
+
function getPluginHookNames(plugin) {
|
|
3201
|
+
const hooks = [];
|
|
3202
|
+
if (plugin.afterInfrastructure) hooks.push("afterInfrastructure");
|
|
3203
|
+
if (plugin.beforeRoutes) hooks.push("beforeRoutes");
|
|
3204
|
+
if (plugin.afterRoutes) hooks.push("afterRoutes");
|
|
3205
|
+
if (plugin.afterStart) hooks.push("afterStart");
|
|
3206
|
+
if (plugin.beforeShutdown) hooks.push("beforeShutdown");
|
|
3207
|
+
return hooks;
|
|
3208
|
+
}
|
|
3209
|
+
async function executePluginHooks(plugins, hookName, ...args) {
|
|
3210
|
+
for (const plugin of plugins) {
|
|
3211
|
+
const hook = plugin[hookName];
|
|
3212
|
+
if (typeof hook === "function") {
|
|
3213
|
+
try {
|
|
3214
|
+
pluginLogger.debug("Executing plugin hook", {
|
|
3215
|
+
plugin: plugin.name,
|
|
3216
|
+
hook: hookName
|
|
3217
|
+
});
|
|
3218
|
+
await hook(...args);
|
|
3219
|
+
} catch (error) {
|
|
3220
|
+
pluginLogger.error("Plugin hook failed", {
|
|
3221
|
+
plugin: plugin.name,
|
|
3222
|
+
hook: hookName,
|
|
3223
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
3224
|
+
});
|
|
3225
|
+
throw new Error(
|
|
3226
|
+
`Plugin ${plugin.name} failed in ${hookName} hook: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
3227
|
+
);
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3119
3233
|
// src/server/create-server.ts
|
|
3120
3234
|
var serverLogger = logger.child("server");
|
|
3121
|
-
async function createServer(config) {
|
|
3235
|
+
async function createServer(config, plugins = []) {
|
|
3122
3236
|
const cwd = process.cwd();
|
|
3123
3237
|
const appPath = join(cwd, "src", "server", "app.ts");
|
|
3124
3238
|
const appJsPath = join(cwd, "src", "server", "app.js");
|
|
3125
3239
|
if (existsSync(appPath) || existsSync(appJsPath)) {
|
|
3126
|
-
return await loadCustomApp(appPath, appJsPath, config);
|
|
3240
|
+
return await loadCustomApp(appPath, appJsPath, config, plugins);
|
|
3127
3241
|
}
|
|
3128
|
-
return await createAutoConfiguredApp(config);
|
|
3242
|
+
return await createAutoConfiguredApp(config, plugins);
|
|
3129
3243
|
}
|
|
3130
|
-
async function loadCustomApp(appPath, appJsPath, config) {
|
|
3244
|
+
async function loadCustomApp(appPath, appJsPath, config, plugins = []) {
|
|
3131
3245
|
const appModule = await (existsSync(appPath) ? import(appPath) : import(appJsPath));
|
|
3132
3246
|
const appFactory = appModule.default;
|
|
3133
3247
|
if (!appFactory) {
|
|
3134
3248
|
throw new Error("app.ts must export a default function that returns a Hono app");
|
|
3135
3249
|
}
|
|
3136
3250
|
const app = await appFactory();
|
|
3251
|
+
await executePluginHooks(plugins, "beforeRoutes", app);
|
|
3137
3252
|
const debug = config?.debug ?? process.env.NODE_ENV === "development";
|
|
3138
3253
|
await loadRoutes(app, { routesDir: config?.routesPath, debug });
|
|
3254
|
+
await executePluginHooks(plugins, "afterRoutes", app);
|
|
3139
3255
|
return app;
|
|
3140
3256
|
}
|
|
3141
|
-
async function createAutoConfiguredApp(config) {
|
|
3257
|
+
async function createAutoConfiguredApp(config, plugins = []) {
|
|
3142
3258
|
const app = new Hono();
|
|
3143
3259
|
const middlewareConfig = config?.middleware ?? {};
|
|
3144
3260
|
const enableLogger = middlewareConfig.logger !== false;
|
|
@@ -3154,8 +3270,10 @@ async function createAutoConfiguredApp(config) {
|
|
|
3154
3270
|
config?.use?.forEach((mw) => app.use("*", mw));
|
|
3155
3271
|
registerHealthCheckEndpoint(app, config);
|
|
3156
3272
|
await executeBeforeRoutesHook(app, config);
|
|
3273
|
+
await executePluginHooks(plugins, "beforeRoutes", app);
|
|
3157
3274
|
await loadAppRoutes(app, config);
|
|
3158
3275
|
await executeAfterRoutesHook(app, config);
|
|
3276
|
+
await executePluginHooks(plugins, "afterRoutes", app);
|
|
3159
3277
|
if (enableErrorHandler) {
|
|
3160
3278
|
app.onError(ErrorHandler());
|
|
3161
3279
|
}
|
|
@@ -3300,9 +3418,17 @@ async function startServer(config) {
|
|
|
3300
3418
|
if (debug) {
|
|
3301
3419
|
logMiddlewareOrder(finalConfig);
|
|
3302
3420
|
}
|
|
3421
|
+
serverLogger2.debug("Discovering plugins...");
|
|
3422
|
+
const plugins = await discoverPlugins();
|
|
3423
|
+
if (plugins.length > 0) {
|
|
3424
|
+
serverLogger2.info("Plugins discovered", {
|
|
3425
|
+
count: plugins.length,
|
|
3426
|
+
plugins: plugins.map((p) => p.name)
|
|
3427
|
+
});
|
|
3428
|
+
}
|
|
3303
3429
|
try {
|
|
3304
|
-
await initializeInfrastructure(finalConfig);
|
|
3305
|
-
const app = await createServer(finalConfig);
|
|
3430
|
+
await initializeInfrastructure(finalConfig, plugins);
|
|
3431
|
+
const app = await createServer(finalConfig, plugins);
|
|
3306
3432
|
const server = startHttpServer(app, host, port);
|
|
3307
3433
|
const timeouts = getTimeoutConfig(finalConfig.timeout);
|
|
3308
3434
|
applyServerTimeouts(server, timeouts);
|
|
@@ -3313,7 +3439,7 @@ async function startServer(config) {
|
|
|
3313
3439
|
port
|
|
3314
3440
|
});
|
|
3315
3441
|
logServerStarted(debug, host, port, finalConfig, timeouts);
|
|
3316
|
-
const shutdownServer = createShutdownHandler(server, finalConfig);
|
|
3442
|
+
const shutdownServer = createShutdownHandler(server, finalConfig, plugins);
|
|
3317
3443
|
const shutdown = createGracefulShutdown(shutdownServer, finalConfig);
|
|
3318
3444
|
registerShutdownHandlers(shutdown);
|
|
3319
3445
|
const serverInstance = {
|
|
@@ -3333,6 +3459,7 @@ async function startServer(config) {
|
|
|
3333
3459
|
serverLogger2.error("afterStart hook failed", error);
|
|
3334
3460
|
}
|
|
3335
3461
|
}
|
|
3462
|
+
await executePluginHooks(plugins, "afterStart", serverInstance);
|
|
3336
3463
|
return serverInstance;
|
|
3337
3464
|
} catch (error) {
|
|
3338
3465
|
const err = error;
|
|
@@ -3374,7 +3501,7 @@ function logMiddlewareOrder(config) {
|
|
|
3374
3501
|
order: middlewareOrder
|
|
3375
3502
|
});
|
|
3376
3503
|
}
|
|
3377
|
-
async function initializeInfrastructure(config) {
|
|
3504
|
+
async function initializeInfrastructure(config, plugins) {
|
|
3378
3505
|
if (config.lifecycle?.beforeInfrastructure) {
|
|
3379
3506
|
serverLogger2.debug("Executing beforeInfrastructure hook...");
|
|
3380
3507
|
try {
|
|
@@ -3407,6 +3534,7 @@ async function initializeInfrastructure(config) {
|
|
|
3407
3534
|
throw new Error("Server initialization failed in afterInfrastructure hook");
|
|
3408
3535
|
}
|
|
3409
3536
|
}
|
|
3537
|
+
await executePluginHooks(plugins, "afterInfrastructure");
|
|
3410
3538
|
}
|
|
3411
3539
|
function startHttpServer(app, host, port) {
|
|
3412
3540
|
serverLogger2.debug(`Starting server on ${host}:${port}...`);
|
|
@@ -3433,7 +3561,7 @@ function logServerStarted(debug, host, port, config, timeouts) {
|
|
|
3433
3561
|
config: startupConfig
|
|
3434
3562
|
});
|
|
3435
3563
|
}
|
|
3436
|
-
function createShutdownHandler(server, config) {
|
|
3564
|
+
function createShutdownHandler(server, config, plugins) {
|
|
3437
3565
|
return async () => {
|
|
3438
3566
|
serverLogger2.debug("Closing HTTP server...");
|
|
3439
3567
|
await new Promise((resolve) => {
|
|
@@ -3450,6 +3578,11 @@ function createShutdownHandler(server, config) {
|
|
|
3450
3578
|
serverLogger2.error("beforeShutdown hook failed", error);
|
|
3451
3579
|
}
|
|
3452
3580
|
}
|
|
3581
|
+
try {
|
|
3582
|
+
await executePluginHooks(plugins, "beforeShutdown");
|
|
3583
|
+
} catch (error) {
|
|
3584
|
+
serverLogger2.error("Plugin beforeShutdown hooks failed", error);
|
|
3585
|
+
}
|
|
3453
3586
|
const shouldCloseDatabase = config.infrastructure?.database !== false;
|
|
3454
3587
|
const shouldCloseRedis = config.infrastructure?.redis !== false;
|
|
3455
3588
|
if (shouldCloseDatabase) {
|
|
@@ -3507,14 +3640,16 @@ function registerShutdownHandlers(shutdown) {
|
|
|
3507
3640
|
} else {
|
|
3508
3641
|
serverLogger2.error("Uncaught exception", error);
|
|
3509
3642
|
}
|
|
3510
|
-
|
|
3643
|
+
serverLogger2.info("Exiting immediately for clean restart");
|
|
3644
|
+
process.exit(1);
|
|
3511
3645
|
});
|
|
3512
3646
|
process.on("unhandledRejection", (reason, promise) => {
|
|
3513
3647
|
serverLogger2.error("Unhandled promise rejection", {
|
|
3514
3648
|
reason,
|
|
3515
3649
|
promise
|
|
3516
3650
|
});
|
|
3517
|
-
|
|
3651
|
+
serverLogger2.info("Exiting immediately for clean restart");
|
|
3652
|
+
process.exit(1);
|
|
3518
3653
|
});
|
|
3519
3654
|
}
|
|
3520
3655
|
async function cleanupOnFailure(config) {
|